home *** CD-ROM | disk | FTP | other *** search
/ Risc World 3 / Risc World 3.iso / SOFTWARE / ISSUE6 / PD / PDF / xpdf / c++ / Gfx < prev    next >
Text File  |  2003-02-24  |  76KB  |  2,957 lines

  1. //========================================================================
  2. //
  3. // Gfx.cc
  4. //
  5. // Copyright 1996-2002 Glyph & Cog, LLC
  6. //
  7. //========================================================================
  8.  
  9. //========================================================================
  10. //
  11. // Changes marked:
  12. //
  13. //   //**** Colin Granville ****
  14. // To
  15. //   //**** end Colin Granville ****
  16. //
  17. // made by Colin Granville 2003
  18. //
  19. //========================================================================
  20.  
  21. #include <aconf.h>
  22.  
  23. #ifdef USE_GCC_PRAGMAS
  24. #pragma implementation
  25. #endif
  26.  
  27. #include <stdio.h>
  28. #include <stddef.h>
  29. #include <string.h>
  30. #include <math.h>
  31. #include "gmem.h"
  32. #include "GlobalParams.h"
  33. #include "CharTypes.h"
  34. #include "Object.h"
  35. #include "Array.h"
  36. #include "Dict.h"
  37. #include "Stream.h"
  38. #include "Lexer.h"
  39. #include "Parser.h"
  40. #include "GfxFont.h"
  41. #include "GfxState.h"
  42. #include "OutputDev.h"
  43. #include "Page.h"
  44. #include "Error.h"
  45. #include "Gfx.h"
  46.  
  47. // the MSVC math.h doesn't define this
  48. #ifndef M_PI
  49. #define M_PI 3.14159265358979323846
  50. #endif
  51.  
  52. //------------------------------------------------------------------------
  53. // constants
  54. //------------------------------------------------------------------------
  55.  
  56. // Max number of splits along the t axis for an axial shading fill.
  57. #define axialMaxSplits 256
  58.  
  59. // Max delta allowed in any color component for an axial shading fill.
  60. #define axialColorDelta (1 / 256.0)
  61.  
  62. // Max number of splits along the t axis for a radial shading fill.
  63. #define radialMaxSplits 256
  64.  
  65. // Max delta allowed in any color component for a radial shading fill.
  66. #define radialColorDelta (1 / 256.0)
  67.  
  68. //------------------------------------------------------------------------
  69. // Operator table
  70. //------------------------------------------------------------------------
  71.  
  72. Operator Gfx::opTab[] = {
  73.   {"\"",  3, {tchkNum,    tchkNum,    tchkString},
  74.           &Gfx::opMoveSetShowText},
  75.   {"'",   1, {tchkString},
  76.           &Gfx::opMoveShowText},
  77.   {"B",   0, {tchkNone},
  78.           &Gfx::opFillStroke},
  79.   {"B*",  0, {tchkNone},
  80.           &Gfx::opEOFillStroke},
  81.   {"BDC", 2, {tchkName,   tchkProps},
  82.           &Gfx::opBeginMarkedContent},
  83.   {"BI",  0, {tchkNone},
  84.           &Gfx::opBeginImage},
  85.   {"BMC", 1, {tchkName},
  86.           &Gfx::opBeginMarkedContent},
  87.   {"BT",  0, {tchkNone},
  88.           &Gfx::opBeginText},
  89.   {"BX",  0, {tchkNone},
  90.           &Gfx::opBeginIgnoreUndef},
  91.   {"CS",  1, {tchkName},
  92.           &Gfx::opSetStrokeColorSpace},
  93.   {"DP",  2, {tchkName,   tchkProps},
  94.           &Gfx::opMarkPoint},
  95.   {"Do",  1, {tchkName},
  96.           &Gfx::opXObject},
  97.   {"EI",  0, {tchkNone},
  98.           &Gfx::opEndImage},
  99.   {"EMC", 0, {tchkNone},
  100.           &Gfx::opEndMarkedContent},
  101.   {"ET",  0, {tchkNone},
  102.           &Gfx::opEndText},
  103.   {"EX",  0, {tchkNone},
  104.           &Gfx::opEndIgnoreUndef},
  105.   {"F",   0, {tchkNone},
  106.           &Gfx::opFill},
  107.   {"G",   1, {tchkNum},
  108.           &Gfx::opSetStrokeGray},
  109.   {"ID",  0, {tchkNone},
  110.           &Gfx::opImageData},
  111.   {"J",   1, {tchkInt},
  112.           &Gfx::opSetLineCap},
  113.   {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
  114.           &Gfx::opSetStrokeCMYKColor},
  115.   {"M",   1, {tchkNum},
  116.           &Gfx::opSetMiterLimit},
  117.   {"MP",  1, {tchkName},
  118.           &Gfx::opMarkPoint},
  119.   {"Q",   0, {tchkNone},
  120.           &Gfx::opRestore},
  121.   {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
  122.           &Gfx::opSetStrokeRGBColor},
  123.   {"S",   0, {tchkNone},
  124.           &Gfx::opStroke},
  125.   {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
  126.           &Gfx::opSetStrokeColor},
  127.   {"SCN", -5, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
  128.                tchkSCN},
  129.           &Gfx::opSetStrokeColorN},
  130.   {"T*",  0, {tchkNone},
  131.           &Gfx::opTextNextLine},
  132.   {"TD",  2, {tchkNum,    tchkNum},
  133.           &Gfx::opTextMoveSet},
  134.   {"TJ",  1, {tchkArray},
  135.           &Gfx::opShowSpaceText},
  136.   {"TL",  1, {tchkNum},
  137.           &Gfx::opSetTextLeading},
  138.   {"Tc",  1, {tchkNum},
  139.           &Gfx::opSetCharSpacing},
  140.   {"Td",  2, {tchkNum,    tchkNum},
  141.           &Gfx::opTextMove},
  142.   {"Tf",  2, {tchkName,   tchkNum},
  143.           &Gfx::opSetFont},
  144.   {"Tj",  1, {tchkString},
  145.           &Gfx::opShowText},
  146.   {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
  147.               tchkNum,    tchkNum},
  148.           &Gfx::opSetTextMatrix},
  149.   {"Tr",  1, {tchkInt},
  150.           &Gfx::opSetTextRender},
  151.   {"Ts",  1, {tchkNum},
  152.           &Gfx::opSetTextRise},
  153.   {"Tw",  1, {tchkNum},
  154.           &Gfx::opSetWordSpacing},
  155.   {"Tz",  1, {tchkNum},
  156.           &Gfx::opSetHorizScaling},
  157.   {"W",   0, {tchkNone},
  158.           &Gfx::opClip},
  159.   {"W*",  0, {tchkNone},
  160.           &Gfx::opEOClip},
  161.   {"b",   0, {tchkNone},
  162.           &Gfx::opCloseFillStroke},
  163.   {"b*",  0, {tchkNone},
  164.           &Gfx::opCloseEOFillStroke},
  165.   {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
  166.               tchkNum,    tchkNum},
  167.           &Gfx::opCurveTo},
  168.   {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
  169.               tchkNum,    tchkNum},
  170.           &Gfx::opConcat},
  171.   {"cs",  1, {tchkName},
  172.           &Gfx::opSetFillColorSpace},
  173.   {"d",   2, {tchkArray,  tchkNum},
  174.           &Gfx::opSetDash},
  175.   {"d0",  2, {tchkNum,    tchkNum},
  176.           &Gfx::opSetCharWidth},
  177.   {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
  178.               tchkNum,    tchkNum},
  179.           &Gfx::opSetCacheDevice},
  180.   {"f",   0, {tchkNone},
  181.           &Gfx::opFill},
  182.   {"f*",  0, {tchkNone},
  183.           &Gfx::opEOFill},
  184.   {"g",   1, {tchkNum},
  185.           &Gfx::opSetFillGray},
  186.   {"gs",  1, {tchkName},
  187.           &Gfx::opSetExtGState},
  188.   {"h",   0, {tchkNone},
  189.           &Gfx::opClosePath},
  190.   {"i",   1, {tchkNum},
  191.           &Gfx::opSetFlat},
  192.   {"j",   1, {tchkInt},
  193.           &Gfx::opSetLineJoin},
  194.   {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
  195.           &Gfx::opSetFillCMYKColor},
  196.   {"l",   2, {tchkNum,    tchkNum},
  197.           &Gfx::opLineTo},
  198.   {"m",   2, {tchkNum,    tchkNum},
  199.           &Gfx::opMoveTo},
  200.   {"n",   0, {tchkNone},
  201.           &Gfx::opEndPath},
  202.   {"q",   0, {tchkNone},
  203.           &Gfx::opSave},
  204.   {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
  205.           &Gfx::opRectangle},
  206.   {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
  207.           &Gfx::opSetFillRGBColor},
  208.   {"ri",  1, {tchkName},
  209.           &Gfx::opSetRenderingIntent},
  210.   {"s",   0, {tchkNone},
  211.           &Gfx::opCloseStroke},
  212.   {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
  213.           &Gfx::opSetFillColor},
  214.   {"scn", -5, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
  215.                tchkSCN},
  216.           &Gfx::opSetFillColorN},
  217.   {"sh",  1, {tchkName},
  218.           &Gfx::opShFill},
  219.   {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
  220.           &Gfx::opCurveTo1},
  221.   {"w",   1, {tchkNum},
  222.           &Gfx::opSetLineWidth},
  223.   {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
  224.           &Gfx::opCurveTo2},
  225. };
  226.  
  227. #define numOps (sizeof(opTab) / sizeof(Operator))
  228.  
  229. //------------------------------------------------------------------------
  230. // GfxResources
  231. //------------------------------------------------------------------------
  232.  
  233. GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
  234.   Object obj1;
  235.  
  236.   if (resDict) {
  237.  
  238.     // build font dictionary
  239.     fonts = NULL;
  240.     resDict->lookup("Font", &obj1);
  241.     if (obj1.isDict()) {
  242.       fonts = new GfxFontDict(xref, obj1.getDict());
  243.     }
  244.     obj1.free();
  245.  
  246.     // get XObject dictionary
  247.     resDict->lookup("XObject", &xObjDict);
  248.  
  249.     // get color space dictionary
  250.     resDict->lookup("ColorSpace", &colorSpaceDict);
  251.  
  252.     // get pattern dictionary
  253.     resDict->lookup("Pattern", &patternDict);
  254.  
  255.     // get shading dictionary
  256.     resDict->lookup("Shading", &shadingDict);
  257.  
  258.     // get graphics state parameter dictionary
  259.     resDict->lookup("ExtGState", &gStateDict);
  260.  
  261.   } else {
  262.     fonts = NULL;
  263.     xObjDict.initNull();
  264.     colorSpaceDict.initNull();
  265.     patternDict.initNull();
  266.     gStateDict.initNull();
  267.   }
  268.  
  269.   next = nextA;
  270. }
  271.  
  272. GfxResources::~GfxResources() {
  273.   if (fonts) {
  274.     delete fonts;
  275.   }
  276.   xObjDict.free();
  277.   colorSpaceDict.free();
  278.   patternDict.free();
  279.   shadingDict.free();
  280.   gStateDict.free();
  281. }
  282.  
  283. GfxFont *GfxResources::lookupFont(char *name) {
  284.   GfxFont *font;
  285.   GfxResources *resPtr;
  286.  
  287.   for (resPtr = this; resPtr; resPtr = resPtr->next) {
  288.     if (resPtr->fonts) {
  289.       if ((font = resPtr->fonts->lookup(name)))
  290.         return font;
  291.     }
  292.   }
  293.   error(-1, "Unknown font tag '%s'", name);
  294.   return NULL;
  295. }
  296.  
  297. GBool GfxResources::lookupXObject(char *name, Object *obj) {
  298.   GfxResources *resPtr;
  299.  
  300.   for (resPtr = this; resPtr; resPtr = resPtr->next) {
  301.     if (resPtr->xObjDict.isDict()) {
  302.       if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
  303.         return gTrue;
  304.       obj->free();
  305.     }
  306.   }
  307.   error(-1, "XObject '%s' is unknown", name);
  308.   return gFalse;
  309. }
  310.  
  311. GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
  312.   GfxResources *resPtr;
  313.  
  314.   for (resPtr = this; resPtr; resPtr = resPtr->next) {
  315.     if (resPtr->xObjDict.isDict()) {
  316.       if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
  317.         return gTrue;
  318.       obj->free();
  319.     }
  320.   }
  321.   error(-1, "XObject '%s' is unknown", name);
  322.   return gFalse;
  323. }
  324.  
  325. void GfxResources::lookupColorSpace(char *name, Object *obj) {
  326.   GfxResources *resPtr;
  327.  
  328.   for (resPtr = this; resPtr; resPtr = resPtr->next) {
  329.     if (resPtr->colorSpaceDict.isDict()) {
  330.       if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
  331.         return;
  332.       }
  333.       obj->free();
  334.     }
  335.   }
  336.   obj->initNull();
  337. }
  338.  
  339. GfxPattern *GfxResources::lookupPattern(char *name) {
  340.   GfxResources *resPtr;
  341.   GfxPattern *pattern;
  342.   Object obj;
  343.  
  344.   for (resPtr = this; resPtr; resPtr = resPtr->next) {
  345.     if (resPtr->patternDict.isDict()) {
  346.       if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
  347.         pattern = GfxPattern::parse(&obj);
  348.         obj.free();
  349.         return pattern;
  350.       }
  351.       obj.free();
  352.     }
  353.   }
  354.   error(-1, "Unknown pattern '%s'", name);
  355.   return NULL;
  356. }
  357.  
  358. GfxShading *GfxResources::lookupShading(char *name) {
  359.   GfxResources *resPtr;
  360.   GfxShading *shading;
  361.   Object obj;
  362.  
  363.   for (resPtr = this; resPtr; resPtr = resPtr->next) {
  364.     if (resPtr->shadingDict.isDict()) {
  365.       if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
  366.         shading = GfxShading::parse(&obj);
  367.         obj.free();
  368.         return shading;
  369.       }
  370.       obj.free();
  371.     }
  372.   }
  373.   error(-1, "Unknown shading '%s'", name);
  374.   return NULL;
  375. }
  376.  
  377. GBool GfxResources::lookupGState(char *name, Object *obj) {
  378.   GfxResources *resPtr;
  379.  
  380.   for (resPtr = this; resPtr; resPtr = resPtr->next) {
  381.     if (resPtr->gStateDict.isDict()) {
  382.       if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
  383.         return gTrue;
  384.       }
  385.       obj->free();
  386.     }
  387.   }
  388.   error(-1, "ExtGState '%s' is unknown", name);
  389.   return gFalse;
  390. }
  391.  
  392. //------------------------------------------------------------------------
  393. // Gfx
  394. //------------------------------------------------------------------------
  395.  
  396. Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, double dpi,
  397.          PDFRectangle *box, GBool crop, PDFRectangle *cropBox, int rotate,
  398.          GBool (*abortCheckCbkA)(void *data),
  399.          void *abortCheckCbkDataA) {
  400.   int i;
  401.  
  402.   xref = xrefA;
  403.   subPage = gFalse;
  404.   printCommands = globalParams->getPrintCommands();
  405.  
  406.   // start the resource stack
  407.   res = new GfxResources(xref, resDict, NULL);
  408.  
  409.   // initialize
  410.   out = outA;
  411.   state = new GfxState(dpi, box, rotate, out->upsideDown());
  412.   fontChanged = gFalse;
  413.   clip = clipNone;
  414.   ignoreUndef = 0;
  415.   out->startPage(pageNum, state);
  416.   out->setDefaultCTM(state->getCTM());
  417.   out->updateAll(state);
  418.   for (i = 0; i < 6; ++i) {
  419.     baseMatrix[i] = state->getCTM()[i];
  420.   }
  421.   abortCheckCbk = abortCheckCbkA;
  422.   abortCheckCbkData = abortCheckCbkDataA;
  423.  
  424.   // set crop box
  425.   if (crop) {
  426.     state->moveTo(cropBox->x1, cropBox->y1);
  427.     state->lineTo(cropBox->x2, cropBox->y1);
  428.     state->lineTo(cropBox->x2, cropBox->y2);
  429.     state->lineTo(cropBox->x1, cropBox->y2);
  430.     state->closePath();
  431.     state->clip();
  432.     out->clip(state);
  433.     state->clearPath();
  434.   }
  435. }
  436.  
  437. Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
  438.          PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
  439.          GBool (*abortCheckCbkA)(void *data),
  440.          void *abortCheckCbkDataA) {
  441.   int i;
  442.  
  443.   xref = xrefA;
  444.   subPage = gTrue;
  445.   printCommands = globalParams->getPrintCommands();
  446.  
  447.   // start the resource stack
  448.   res = new GfxResources(xref, resDict, NULL);
  449.  
  450.   // initialize
  451.   out = outA;
  452.   state = new GfxState(72, box, 0, gFalse);
  453.   fontChanged = gFalse;
  454.   clip = clipNone;
  455.   ignoreUndef = 0;
  456.   for (i = 0; i < 6; ++i) {
  457.     baseMatrix[i] = state->getCTM()[i];
  458.   }
  459.   abortCheckCbk = abortCheckCbkA;
  460.   abortCheckCbkData = abortCheckCbkDataA;
  461.  
  462.   // set crop box
  463.   if (crop) {
  464.     state->moveTo(cropBox->x1, cropBox->y1);
  465.     state->lineTo(cropBox->x2, cropBox->y1);
  466.     state->lineTo(cropBox->x2, cropBox->y2);
  467.     state->lineTo(cropBox->x1, cropBox->y2);
  468.     state->closePath();
  469.     state->clip();
  470.     out->clip(state);
  471.     state->clearPath();
  472.   }
  473. }
  474.  
  475. Gfx::~Gfx() {
  476.   while (state->hasSaves()) {
  477.     state = state->restore();
  478.     out->restoreState(state);
  479.   }
  480.   if (!subPage) {
  481.     out->endPage();
  482.   }
  483.   while (res) {
  484.     popResources();
  485.   }
  486.   if (state) {
  487.     delete state;
  488.   }
  489. }
  490.  
  491. void Gfx::display(Object *obj, GBool topLevel) {
  492.   Object obj2;
  493.   int i;
  494.  
  495.   if (obj->isArray()) {
  496.     for (i = 0; i < obj->arrayGetLength(); ++i) {
  497.       obj->arrayGet(i, &obj2);
  498.       if (!obj2.isStream()) {
  499.         error(-1, "Weird page contents");
  500.         obj2.free();
  501.         return;
  502.       }
  503.       obj2.free();
  504.     }
  505.   } else if (!obj->isStream()) {
  506.     error(-1, "Weird page contents");
  507.     return;
  508.   }
  509.   parser = new Parser(xref, new Lexer(xref, obj));
  510.   go(topLevel);
  511.   delete parser;
  512.   parser = NULL;
  513. }
  514.  
  515. void Gfx::go(GBool topLevel) {
  516.   Object obj;
  517.   Object args[maxArgs];
  518.   int numArgs, i;
  519.   int lastAbortCheck;
  520.  
  521.   // scan a sequence of objects
  522.   updateLevel = lastAbortCheck = 0;
  523.   numArgs = 0;
  524.   parser->getObj(&obj);
  525.   while (!obj.isEOF()) {
  526.  
  527.     // got a command - execute it
  528.     if (obj.isCmd()) {
  529.       if (printCommands) {
  530.         obj.print(stdout);
  531.         for (i = 0; i < numArgs; ++i) {
  532.           printf(" ");
  533.           args[i].print(stdout);
  534.         }
  535.         printf("\n");
  536.         fflush(stdout);
  537.       }
  538.       execOp(&obj, args, numArgs);
  539.       obj.free();
  540.       for (i = 0; i < numArgs; ++i)
  541.         args[i].free();
  542.       numArgs = 0;
  543.  
  544.       // periodically update display
  545.       if (++updateLevel >= 20000) {
  546.         out->dump();
  547.         updateLevel = 0;
  548.       }
  549.  
  550.       // check for an abort
  551.       if (abortCheckCbk) {
  552.         if (updateLevel - lastAbortCheck > 10) {
  553.           if ((*abortCheckCbk)(abortCheckCbkData)) {
  554.             break;
  555.           }
  556.           lastAbortCheck = updateLevel;
  557.         }
  558.       }
  559.  
  560.     // got an argument - save it
  561.     } else if (numArgs < maxArgs) {
  562.       args[numArgs++] = obj;
  563.  
  564.     // too many arguments - something is wrong
  565.     } else {
  566.       error(getPos(), "Too many args in content stream");
  567.       if (printCommands) {
  568.         printf("throwing away arg: ");
  569.         obj.print(stdout);
  570.         printf("\n");
  571.         fflush(stdout);
  572.       }
  573.       obj.free();
  574.     }
  575.  
  576.     // grab the next object
  577.     parser->getObj(&obj);
  578.   }
  579.   obj.free();
  580.  
  581.   // args at end with no command
  582.   if (numArgs > 0) {
  583.     error(getPos(), "Leftover args in content stream");
  584.     if (printCommands) {
  585.       printf("%d leftovers:", numArgs);
  586.       for (i = 0; i < numArgs; ++i) {
  587.         printf(" ");
  588.         args[i].print(stdout);
  589.       }
  590.       printf("\n");
  591.       fflush(stdout);
  592.     }
  593.     for (i = 0; i < numArgs; ++i)
  594.       args[i].free();
  595.   }
  596.  
  597.   // update display
  598.   if (topLevel && updateLevel > 0) {
  599.     out->dump();
  600.   }
  601. }
  602.  
  603. void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
  604.   Operator *op;
  605.   char *name;
  606.   int i;
  607.  
  608.   // find operator
  609.   name = cmd->getCmd();
  610.   if (!(op = findOp(name))) {
  611.     if (ignoreUndef == 0)
  612.       error(getPos(), "Unknown operator '%s'", name);
  613.     return;
  614.   }
  615.  
  616.   // type check args
  617.   if (op->numArgs >= 0) {
  618.     if (numArgs != op->numArgs) {
  619.       error(getPos(), "Wrong number (%d) of args to '%s' operator",
  620.             numArgs, name);
  621.       return;
  622.     }
  623.   } else {
  624.     if (numArgs > -op->numArgs) {
  625.       error(getPos(), "Too many (%d) args to '%s' operator",
  626.             numArgs, name);
  627.       return;
  628.     }
  629.   }
  630.   for (i = 0; i < numArgs; ++i) {
  631.     if (!checkArg(&args[i], op->tchk[i])) {
  632.       error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
  633.             i, name, args[i].getTypeName());
  634.       return;
  635.     }
  636.   }
  637.  
  638.   // do it
  639.   (this->*op->func)(args, numArgs);
  640. }
  641.  
  642. Operator *Gfx::findOp(char *name) {
  643.   int a, b, m, cmp;
  644.  
  645.   a = -1;
  646.   b = numOps;
  647.   // invariant: opTab[a] < name < opTab[b]
  648.   while (b - a > 1) {
  649.     m = (a + b) / 2;
  650.     cmp = strcmp(opTab[m].name, name);
  651.     if (cmp < 0)
  652.       a = m;
  653.     else if (cmp > 0)
  654.       b = m;
  655.     else
  656.       a = b = m;
  657.   }
  658.   if (cmp != 0)
  659.     return NULL;
  660.   return &opTab[a];
  661. }
  662.  
  663. GBool Gfx::checkArg(Object *arg, TchkType type) {
  664.   switch (type) {
  665.   case tchkBool:   return arg->isBool();
  666.   case tchkInt:    return arg->isInt();
  667.   case tchkNum:    return arg->isNum();
  668.   case tchkString: return arg->isString();
  669.   case tchkName:   return arg->isName();
  670.   case tchkArray:  return arg->isArray();
  671.   case tchkProps:  return arg->isDict() || arg->isName();
  672.   case tchkSCN:    return arg->isNum() || arg->isName();
  673.   case tchkNone:   return gFalse;
  674.   }
  675.   return gFalse;
  676. }
  677.  
  678. int Gfx::getPos() {
  679.   return parser ? parser->getPos() : -1;
  680. }
  681.  
  682. //------------------------------------------------------------------------
  683. // graphics state operators
  684. //------------------------------------------------------------------------
  685.  
  686. void Gfx::opSave(Object args[], int numArgs) {
  687.   out->saveState(state);
  688.   state = state->save();
  689. }
  690.  
  691. void Gfx::opRestore(Object args[], int numArgs) {
  692.   state = state->restore();
  693.   out->restoreState(state);
  694. }
  695.  
  696. void Gfx::opConcat(Object args[], int numArgs) {
  697.   state->concatCTM(args[0].getNum(), args[1].getNum(),
  698.                    args[2].getNum(), args[3].getNum(),
  699.                    args[4].getNum(), args[5].getNum());
  700.   out->updateCTM(state, args[0].getNum(), args[1].getNum(),
  701.                  args[2].getNum(), args[3].getNum(),
  702.                  args[4].getNum(), args[5].getNum());
  703.   fontChanged = gTrue;
  704. }
  705.  
  706. void Gfx::opSetDash(Object args[], int numArgs) {
  707.   Array *a;
  708.   int length;
  709.   Object obj;
  710.   double *dash;
  711.   int i;
  712.  
  713.   a = args[0].getArray();
  714.   length = a->getLength();
  715.   if (length == 0) {
  716.     dash = NULL;
  717.   } else {
  718.     dash = (double *)gmalloc(length * sizeof(double));
  719.     for (i = 0; i < length; ++i) {
  720.       dash[i] = a->get(i, &obj)->getNum();
  721.       obj.free();
  722.     }
  723.   }
  724.   state->setLineDash(dash, length, args[1].getNum());
  725.   out->updateLineDash(state);
  726. }
  727.  
  728. void Gfx::opSetFlat(Object args[], int numArgs) {
  729.   state->setFlatness((int)args[0].getNum());
  730.   out->updateFlatness(state);
  731. }
  732.  
  733. void Gfx::opSetLineJoin(Object args[], int numArgs) {
  734.   state->setLineJoin(args[0].getInt());
  735.   out->updateLineJoin(state);
  736. }
  737.  
  738. void Gfx::opSetLineCap(Object args[], int numArgs) {
  739.   state->setLineCap(args[0].getInt());
  740.   out->updateLineCap(state);
  741. }
  742.  
  743. void Gfx::opSetMiterLimit(Object args[], int numArgs) {
  744.   state->setMiterLimit(args[0].getNum());
  745.   out->updateMiterLimit(state);
  746. }
  747.  
  748. void Gfx::opSetLineWidth(Object args[], int numArgs) {
  749.   state->setLineWidth(args[0].getNum());
  750.   out->updateLineWidth(state);
  751. }
  752.  
  753. void Gfx::opSetExtGState(Object args[], int numArgs) {
  754.   Object obj1, obj2;
  755.  
  756.   if (!res->lookupGState(args[0].getName(), &obj1)) {
  757.     return;
  758.   }
  759.   if (!obj1.isDict()) {
  760.     error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
  761.     obj1.free();
  762.     return;
  763.   }
  764.   if (obj1.dictLookup("ca", &obj2)->isNum()) {
  765.     state->setFillOpacity(obj2.getNum());
  766.     out->updateFillOpacity(state);
  767.   }
  768.   obj2.free();
  769.   if (obj1.dictLookup("CA", &obj2)->isNum()) {
  770.     state->setStrokeOpacity(obj2.getNum());
  771.     out->updateStrokeOpacity(state);
  772.   }
  773.   obj2.free();
  774.   obj1.free();
  775. }
  776.  
  777. void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
  778. }
  779.  
  780. //------------------------------------------------------------------------
  781. // color operators
  782. //------------------------------------------------------------------------
  783.  
  784. void Gfx::opSetFillGray(Object args[], int numArgs) {
  785.   GfxColor color;
  786.  
  787.   state->setFillPattern(NULL);
  788.   state->setFillColorSpace(new GfxDeviceGrayColorSpace());
  789.   color.c[0] = args[0].getNum();
  790.   state->setFillColor(&color);
  791.   out->updateFillColor(state);
  792. }
  793.  
  794. void Gfx::opSetStrokeGray(Object args[], int numArgs) {
  795.   GfxColor color;
  796.  
  797.   state->setStrokePattern(NULL);
  798.   state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
  799.   color.c[0] = args[0].getNum();
  800.   state->setStrokeColor(&color);
  801.   out->updateStrokeColor(state);
  802. }
  803.  
  804. void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
  805.   GfxColor color;
  806.   int i;
  807.  
  808.   state->setFillPattern(NULL);
  809.   state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
  810.   for (i = 0; i < 4; ++i) {
  811.     color.c[i] = args[i].getNum();
  812.   }
  813.   state->setFillColor(&color);
  814.   out->updateFillColor(state);
  815. }
  816.  
  817. void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
  818.   GfxColor color;
  819.   int i;
  820.  
  821.   state->setStrokePattern(NULL);
  822.   state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
  823.   for (i = 0; i < 4; ++i) {
  824.     color.c[i] = args[i].getNum();
  825.   }
  826.   state->setStrokeColor(&color);
  827.   out->updateStrokeColor(state);
  828. }
  829.  
  830. void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
  831.   GfxColor color;
  832.   int i;
  833.  
  834.   state->setFillPattern(NULL);
  835.   state->setFillColorSpace(new GfxDeviceRGBColorSpace());
  836.   for (i = 0; i < 3; ++i) {
  837.     color.c[i] = args[i].getNum();
  838.   }
  839.   state->setFillColor(&color);
  840.   out->updateFillColor(state);
  841. }
  842.  
  843. void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
  844.   GfxColor color;
  845.   int i;
  846.  
  847.   state->setStrokePattern(NULL);
  848.   state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
  849.   for (i = 0; i < 3; ++i) {
  850.     color.c[i] = args[i].getNum();
  851.   }
  852.   state->setStrokeColor(&color);
  853.   out->updateStrokeColor(state);
  854. }
  855.  
  856. void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
  857.   Object obj;
  858.   GfxColorSpace *colorSpace;
  859.   GfxColor color;
  860.   int i;
  861.  
  862.   state->setFillPattern(NULL);
  863.   res->lookupColorSpace(args[0].getName(), &obj);
  864.   if (obj.isNull()) {
  865.     colorSpace = GfxColorSpace::parse(&args[0]);
  866.   } else {
  867.     colorSpace = GfxColorSpace::parse(&obj);
  868.   }
  869.   obj.free();
  870.   if (colorSpace) {
  871.     state->setFillColorSpace(colorSpace);
  872.   } else {
  873.     error(getPos(), "Bad color space (fill)");
  874.   }
  875.   for (i = 0; i < gfxColorMaxComps; ++i) {
  876.     color.c[i] = 0;
  877.   }
  878.   state->setFillColor(&color);
  879.   out->updateFillColor(state);
  880. }
  881.  
  882. void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
  883.   Object obj;
  884.   GfxColorSpace *colorSpace;
  885.   GfxColor color;
  886.   int i;
  887.  
  888.   state->setStrokePattern(NULL);
  889.   res->lookupColorSpace(args[0].getName(), &obj);
  890.   if (obj.isNull()) {
  891.     colorSpace = GfxColorSpace::parse(&args[0]);
  892.   } else {
  893.     colorSpace = GfxColorSpace::parse(&obj);
  894.   }
  895.   obj.free();
  896.   if (colorSpace) {
  897.     state->setStrokeColorSpace(colorSpace);
  898.   } else {
  899.     error(getPos(), "Bad color space (stroke)");
  900.   }
  901.   for (i = 0; i < gfxColorMaxComps; ++i) {
  902.     color.c[i] = 0;
  903.   }
  904.   state->setStrokeColor(&color);
  905.   out->updateStrokeColor(state);
  906. }
  907.  
  908. void Gfx::opSetFillColor(Object args[], int numArgs) {
  909.   GfxColor color;
  910.   int i;
  911.  
  912.   state->setFillPattern(NULL);
  913.   for (i = 0; i < numArgs; ++i) {
  914.     color.c[i] = args[i].getNum();
  915.   }
  916.   state->setFillColor(&color);
  917.   out->updateFillColor(state);
  918. }
  919.  
  920. void Gfx::opSetStrokeColor(Object args[], int numArgs) {
  921.   GfxColor color;
  922.   int i;
  923.  
  924.   state->setStrokePattern(NULL);
  925.   for (i = 0; i < numArgs; ++i) {
  926.     color.c[i] = args[i].getNum();
  927.   }
  928.   state->setStrokeColor(&color);
  929.   out->updateStrokeColor(state);
  930. }
  931.  
  932. void Gfx::opSetFillColorN(Object args[], int numArgs) {
  933.   GfxColor color;
  934.   GfxPattern *pattern;
  935.   int i;
  936.  
  937.   if (state->getFillColorSpace()->getMode() == csPattern) {
  938.     if (numArgs > 1) {
  939.       for (i = 0; i < numArgs && i < 4; ++i) {
  940.         if (args[i].isNum()) {
  941.           color.c[i] = args[i].getNum();
  942.         }
  943.       }
  944.       state->setFillColor(&color);
  945.       out->updateFillColor(state);
  946.     }
  947.     if (args[numArgs-1].isName() &&
  948.         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
  949.       state->setFillPattern(pattern);
  950.     }
  951.  
  952.   } else {
  953.     state->setFillPattern(NULL);
  954.     for (i = 0; i < numArgs && i < 4; ++i) {
  955.       if (args[i].isNum()) {
  956.         color.c[i] = args[i].getNum();
  957.       }
  958.     }
  959.     state->setFillColor(&color);
  960.     out->updateFillColor(state);
  961.   }
  962. }
  963.  
  964. void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
  965.   GfxColor color;
  966.   GfxPattern *pattern;
  967.   int i;
  968.  
  969.   if (state->getStrokeColorSpace()->getMode() == csPattern) {
  970.     if (numArgs > 1) {
  971.       for (i = 0; i < numArgs && i < 4; ++i) {
  972.         if (args[i].isNum()) {
  973.           color.c[i] = args[i].getNum();
  974.         }
  975.       }
  976.       state->setStrokeColor(&color);
  977.       out->updateStrokeColor(state);
  978.     }
  979.     if (args[numArgs-1].isName() &&
  980.         (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
  981.       state->setStrokePattern(pattern);
  982.     }
  983.  
  984.   } else {
  985.     state->setStrokePattern(NULL);
  986.     for (i = 0; i < numArgs && i < 4; ++i) {
  987.       if (args[i].isNum()) {
  988.         color.c[i] = args[i].getNum();
  989.       }
  990.     }
  991.     state->setStrokeColor(&color);
  992.     out->updateStrokeColor(state);
  993.   }
  994. }
  995.  
  996. //------------------------------------------------------------------------
  997. // path segment operators
  998. //------------------------------------------------------------------------
  999.  
  1000. void Gfx::opMoveTo(Object args[], int numArgs) {
  1001.   state->moveTo(args[0].getNum(), args[1].getNum());
  1002. }
  1003.  
  1004. void Gfx::opLineTo(Object args[], int numArgs) {
  1005.   if (!state->isCurPt()) {
  1006.     error(getPos(), "No current point in lineto");
  1007.     return;
  1008.   }
  1009.   state->lineTo(args[0].getNum(), args[1].getNum());
  1010. }
  1011.  
  1012. void Gfx::opCurveTo(Object args[], int numArgs) {
  1013.   double x1, y1, x2, y2, x3, y3;
  1014.  
  1015.   if (!state->isCurPt()) {
  1016.     error(getPos(), "No current point in curveto");
  1017.     return;
  1018.   }
  1019.   x1 = args[0].getNum();
  1020.   y1 = args[1].getNum();
  1021.   x2 = args[2].getNum();
  1022.   y2 = args[3].getNum();
  1023.   x3 = args[4].getNum();
  1024.   y3 = args[5].getNum();
  1025.   state->curveTo(x1, y1, x2, y2, x3, y3);
  1026. }
  1027.  
  1028. void Gfx::opCurveTo1(Object args[], int numArgs) {
  1029.   double x1, y1, x2, y2, x3, y3;
  1030.  
  1031.   if (!state->isCurPt()) {
  1032.     error(getPos(), "No current point in curveto1");
  1033.     return;
  1034.   }
  1035.   x1 = state->getCurX();
  1036.   y1 = state->getCurY();
  1037.   x2 = args[0].getNum();
  1038.   y2 = args[1].getNum();
  1039.   x3 = args[2].getNum();
  1040.   y3 = args[3].getNum();
  1041.   state->curveTo(x1, y1, x2, y2, x3, y3);
  1042. }
  1043.  
  1044. void Gfx::opCurveTo2(Object args[], int numArgs) {
  1045.   double x1, y1, x2, y2, x3, y3;
  1046.  
  1047.   if (!state->isCurPt()) {
  1048.     error(getPos(), "No current point in curveto2");
  1049.     return;
  1050.   }
  1051.   x1 = args[0].getNum();
  1052.   y1 = args[1].getNum();
  1053.   x2 = args[2].getNum();
  1054.   y2 = args[3].getNum();
  1055.   x3 = x2;
  1056.   y3 = y2;
  1057.   state->curveTo(x1, y1, x2, y2, x3, y3);
  1058. }
  1059.  
  1060. void Gfx::opRectangle(Object args[], int numArgs) {
  1061.   double x, y, w, h;
  1062.  
  1063.   x = args[0].getNum();
  1064.   y = args[1].getNum();
  1065.   w = args[2].getNum();
  1066.   h = args[3].getNum();
  1067.   state->moveTo(x, y);
  1068.   state->lineTo(x + w, y);
  1069.   state->lineTo(x + w, y + h);
  1070.   state->lineTo(x, y + h);
  1071.   state->closePath();
  1072. }
  1073.  
  1074. void Gfx::opClosePath(Object args[], int numArgs) {
  1075.   if (!state->isCurPt()) {
  1076.     error(getPos(), "No current point in closepath");
  1077.     return;
  1078.   }
  1079.   state->closePath();
  1080. }
  1081.  
  1082. //------------------------------------------------------------------------
  1083. // path painting operators
  1084. //------------------------------------------------------------------------
  1085.  
  1086. void Gfx::opEndPath(Object args[], int numArgs) {
  1087.   doEndPath();
  1088. }
  1089.  
  1090. void Gfx::opStroke(Object args[], int numArgs) {
  1091.   if (!state->isCurPt()) {
  1092.     //error(getPos(), "No path in stroke");
  1093.     return;
  1094.   }
  1095.   if (state->isPath())
  1096.     out->stroke(state);
  1097.   doEndPath();
  1098. }
  1099.  
  1100. void Gfx::opCloseStroke(Object args[], int numArgs) {
  1101.   if (!state->isCurPt()) {
  1102.     //error(getPos(), "No path in closepath/stroke");
  1103.     return;
  1104.   }
  1105.   if (state->isPath()) {
  1106.     state->closePath();
  1107.     out->stroke(state);
  1108.   }
  1109.   doEndPath();
  1110. }
  1111.  
  1112. void Gfx::opFill(Object args[], int numArgs) {
  1113.   if (!state->isCurPt()) {
  1114.     //error(getPos(), "No path in fill");
  1115.     return;
  1116.   }
  1117.   if (state->isPath()) {
  1118.     if (state->getFillColorSpace()->getMode() == csPattern) {
  1119.       doPatternFill(gFalse);
  1120.     } else {
  1121.       out->fill(state);
  1122.     }
  1123.   }
  1124.   doEndPath();
  1125. }
  1126.  
  1127. void Gfx::opEOFill(Object args[], int numArgs) {
  1128.   if (!state->isCurPt()) {
  1129.     //error(getPos(), "No path in eofill");
  1130.     return;
  1131.   }
  1132.   if (state->isPath()) {
  1133.     if (state->getFillColorSpace()->getMode() == csPattern) {
  1134.       doPatternFill(gTrue);
  1135.     } else {
  1136.       out->eoFill(state);
  1137.     }
  1138.   }
  1139.   doEndPath();
  1140. }
  1141.  
  1142. void Gfx::opFillStroke(Object args[], int numArgs) {
  1143.   if (!state->isCurPt()) {
  1144.     //error(getPos(), "No path in fill/stroke");
  1145.     return;
  1146.   }
  1147.   if (state->isPath()) {
  1148.     if (state->getFillColorSpace()->getMode() == csPattern) {
  1149.       doPatternFill(gFalse);
  1150.     } else {
  1151.       out->fill(state);
  1152.     }
  1153.     out->stroke(state);
  1154.   }
  1155.   doEndPath();
  1156. }
  1157.  
  1158. void Gfx::opCloseFillStroke(Object args[], int numArgs) {
  1159.   if (!state->isCurPt()) {
  1160.     //error(getPos(), "No path in closepath/fill/stroke");
  1161.     return;
  1162.   }
  1163.   if (state->isPath()) {
  1164.     state->closePath();
  1165.     if (state->getFillColorSpace()->getMode() == csPattern) {
  1166.       doPatternFill(gFalse);
  1167.     } else {
  1168.       out->fill(state);
  1169.     }
  1170.     out->stroke(state);
  1171.   }
  1172.   doEndPath();
  1173. }
  1174.  
  1175. void Gfx::opEOFillStroke(Object args[], int numArgs) {
  1176.   if (!state->isCurPt()) {
  1177.     //error(getPos(), "No path in eofill/stroke");
  1178.     return;
  1179.   }
  1180.   if (state->isPath()) {
  1181.     if (state->getFillColorSpace()->getMode() == csPattern) {
  1182.       doPatternFill(gTrue);
  1183.     } else {
  1184.       out->eoFill(state);
  1185.     }
  1186.     out->stroke(state);
  1187.   }
  1188.   doEndPath();
  1189. }
  1190.  
  1191. void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
  1192.   if (!state->isCurPt()) {
  1193.     //error(getPos(), "No path in closepath/eofill/stroke");
  1194.     return;
  1195.   }
  1196.   if (state->isPath()) {
  1197.     state->closePath();
  1198.     if (state->getFillColorSpace()->getMode() == csPattern) {
  1199.       doPatternFill(gTrue);
  1200.     } else {
  1201.       out->eoFill(state);
  1202.     }
  1203.     out->stroke(state);
  1204.   }
  1205.   doEndPath();
  1206. }
  1207.  
  1208. void Gfx::doPatternFill(GBool eoFill) {
  1209.   GfxPatternColorSpace *patCS;
  1210.   GfxPattern *pattern;
  1211.   GfxTilingPattern *tPat;
  1212.   GfxColorSpace *cs;
  1213.  
  1214. //**** Colin Granville ****
  1215. #ifdef ACORN
  1216. //doPatternFill doesn't work for ACORN
  1217. // it's far too slow and clipping doesn't work properly
  1218. // fill area with mid grey
  1219. // it's a bodge but it helps. (it will show black on white and white on black)
  1220.  
  1221.   cs= new GfxDeviceGrayColorSpace;
  1222.   if (!cs) return;
  1223.   out->saveState(state);
  1224.   state = state->save();
  1225.  
  1226.   GfxColor col;
  1227.   col.c[0]=0.707;
  1228.  
  1229.   state->setFillPattern(NULL);
  1230.   state->setFillColor(&col);
  1231.   state->setFillColorSpace(cs);
  1232.  
  1233.   out->updateFillColor(state);
  1234.  
  1235.   if (eoFill) out->eoFill(state); else out->fill(state);
  1236.  
  1237.   state = state->restore();
  1238.   out->restoreState(state);
  1239.  return;
  1240. #endif
  1241.  
  1242. //**** end Colin Granville ****
  1243.   double xMin, yMin, xMax, yMax, x, y, x1, y1;
  1244.   double cxMin, cyMin, cxMax, cyMax;
  1245.   int xi0, yi0, xi1, yi1, xi, yi;
  1246.   double *ctm, *btm, *ptm;
  1247.   double m[6], ictm[6], m1[6], imb[6];
  1248.   double det;
  1249.   double xstep, ystep;
  1250.   int i;
  1251.  
  1252.   // this is a bit of a kludge -- patterns can be really slow, so we
  1253.   // skip them if we're only doing text extraction, since they almost
  1254.   // certainly don't contain any text
  1255.   if (!out->needNonText()) {
  1256.     return;
  1257.   }
  1258.  
  1259.   // get color space
  1260.   patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
  1261.  
  1262.   // get pattern
  1263.   if (!(pattern = state->getFillPattern())) {
  1264.     return;
  1265.   }
  1266.   if (pattern->getType() != 1) {
  1267.     return;
  1268.   }
  1269.   tPat = (GfxTilingPattern *)pattern;
  1270.  
  1271.   // construct a (pattern space) -> (current space) transform matrix
  1272.   ctm = state->getCTM();
  1273.   btm = baseMatrix;
  1274.   ptm = tPat->getMatrix();
  1275.   // iCTM = invert CTM
  1276.   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
  1277.   ictm[0] = ctm[3] * det;
  1278.   ictm[1] = -ctm[1] * det;
  1279.   ictm[2] = -ctm[2] * det;
  1280.   ictm[3] = ctm[0] * det;
  1281.   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
  1282.   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
  1283.   // m1 = PTM * BTM = PTM * base transform matrix
  1284.   m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
  1285.   m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
  1286.   m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
  1287.   m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
  1288.   m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
  1289.   m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
  1290.   // m = m1 * iCTM = (PTM * BTM) * (iCTM)
  1291.   m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
  1292.   m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
  1293.   m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
  1294.   m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
  1295.   m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
  1296.   m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
  1297.  
  1298.   // construct a (base space) -> (pattern space) transform matrix
  1299.   det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
  1300.   imb[0] = m1[3] * det;
  1301.   imb[1] = -m1[1] * det;
  1302.   imb[2] = -m1[2] * det;
  1303.   imb[3] = m1[0] * det;
  1304.   imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
  1305.   imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
  1306.  
  1307.   // save current graphics state
  1308.   out->saveState(state);
  1309.   state = state->save();
  1310.  
  1311.   // set underlying color space (for uncolored tiling patterns)
  1312.   if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
  1313.     state->setFillColorSpace(cs->copy());
  1314.   } else {
  1315.     state->setFillColorSpace(new GfxDeviceGrayColorSpace());
  1316.   }
  1317.   state->setFillPattern(NULL);
  1318.   out->updateFillColor(state);
  1319.  
  1320.   // clip to current path
  1321.   state->clip();
  1322.   if (eoFill) {
  1323.     out->eoClip(state);
  1324.   } else {
  1325.     out->clip(state);
  1326.   }
  1327.   state->clearPath();
  1328.  
  1329.   // transform clip region bbox to pattern space
  1330.   state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
  1331.   xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
  1332.   yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
  1333.   x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
  1334.   y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
  1335.   if (x1 < xMin) {
  1336.     xMin = x1;
  1337.   } else if (x1 > xMax) {
  1338.     xMax = x1;
  1339.   }
  1340.   if (y1 < yMin) {
  1341.     yMin = y1;
  1342.   } else if (y1 > yMax) {
  1343.     yMax = y1;
  1344.   }
  1345.   x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
  1346.   y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
  1347.   if (x1 < xMin) {
  1348.     xMin = x1;
  1349.   } else if (x1 > xMax) {
  1350.     xMax = x1;
  1351.   }
  1352.   if (y1 < yMin) {
  1353.     yMin = y1;
  1354.   } else if (y1 > yMax) {
  1355.     yMax = y1;
  1356.   }
  1357.   x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
  1358.   y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
  1359.   if (x1 < xMin) {
  1360.     xMin = x1;
  1361.   } else if (x1 > xMax) {
  1362.     xMax = x1;
  1363.   }
  1364.   if (y1 < yMin) {
  1365.     yMin = y1;
  1366.   } else if (y1 > yMax) {
  1367.     yMax = y1;
  1368.   }
  1369.  
  1370.   // draw the pattern
  1371.   //~ this should treat negative steps differently -- start at right/top
  1372.   //~ edge instead of left/bottom (?)
  1373.   xstep = fabs(tPat->getXStep());
  1374.   ystep = fabs(tPat->getYStep());
  1375.   xi0 = (int)floor(xMin / xstep);
  1376.   xi1 = (int)ceil(xMax / xstep);
  1377.   yi0 = (int)floor(yMin / ystep);
  1378.   yi1 = (int)ceil(yMax / ystep);
  1379.   for (i = 0; i < 4; ++i) {
  1380.     m1[i] = m[i];
  1381.   }
  1382.   for (yi = yi0; yi < yi1; ++yi) {
  1383.     for (xi = xi0; xi < xi1; ++xi) {
  1384.       x = xi * xstep;
  1385.       y = yi * ystep;
  1386.       m1[4] = x * m[0] + y * m[2] + m[4];
  1387.       m1[5] = x * m[1] + y * m[3] + m[5];
  1388.       doForm1(tPat->getContentStream(), tPat->getResDict(),
  1389.               m1, tPat->getBBox());
  1390.     }
  1391.   }
  1392.  
  1393.   // restore graphics state
  1394.   state = state->restore();
  1395.   out->restoreState(state);
  1396. }
  1397.  
  1398. //**** Colin Granville ****
  1399. #ifdef ACORN
  1400.  
  1401. void trans(double* ictm,double x,double y,double& ox,double& oy)
  1402. {
  1403.   ox = x * ictm[0] + y * ictm[2] + ictm[4];
  1404.   oy = x * ictm[1] + y * ictm[3] + ictm[5];
  1405. }
  1406.  
  1407. #endif
  1408.  
  1409. //**** end Colin Granville ****
  1410.  
  1411. void Gfx::opShFill(Object args[], int numArgs) {
  1412.   GfxShading *shading;
  1413.   double xMin, yMin, xMax, yMax;
  1414.  
  1415.   if (!(shading = res->lookupShading(args[0].getName()))) {
  1416.     return;
  1417.   }
  1418.  
  1419. //**** Colin Granville ****
  1420. #ifdef ACORN
  1421. // Shading is far too slow and clipping doesn't work properly
  1422. // anyway so try and fill with a suitable fill colour 
  1423.  
  1424.   out->saveState(state);
  1425.   state = state->save();
  1426.  
  1427.  
  1428.   GfxColor col;
  1429.   switch (shading->getType()) 
  1430.   {
  1431.     case 2:
  1432.       {
  1433.        state->setFillColorSpace(shading->getColorSpace()->copy());
  1434.        GfxAxialShading* sh=(GfxAxialShading *)shading;
  1435.        sh->getColor((sh->getDomain0()+sh->getDomain1())/2,&col);
  1436.       }
  1437.       break;
  1438.     case 3:
  1439.       {
  1440.        state->setFillColorSpace(shading->getColorSpace()->copy());
  1441.        GfxRadialShading* sh=(GfxRadialShading *)shading;
  1442.        sh->getColor((sh->getDomain0()+sh->getDomain1())/2,&col);
  1443.       }
  1444.       break;
  1445.     default:
  1446.        // default to a mid grey shape.
  1447.        state->setFillColorSpace(new GfxDeviceGrayColorSpace);  
  1448.        col.c[0]=0.707;
  1449.   }
  1450.  
  1451.   state->setFillColor(&col);
  1452.   out->updateFillColor(state);
  1453.  
  1454.   // draw box size of clip area transformed so that when transformed by outputdev
  1455.   // it is rectangular - clipping only works when the transformed shape is
  1456.   // a rectangle bigger or same size as clip bounding box
  1457.  
  1458.   double* ctm=state->getCTM();
  1459.   double ictm[6];
  1460.   double det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
  1461.   ictm[0] = ctm[3] * det;
  1462.   ictm[1] = -ctm[1] * det;
  1463.   ictm[2] = -ctm[2] * det;
  1464.   ictm[3] = ctm[0] * det;
  1465.   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
  1466.   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
  1467.   state->getClipBBox(&xMin, &yMin, &xMax, &yMax);
  1468.   xMin-=72;
  1469.   yMin-=72;
  1470.   xMax+=72;
  1471.   yMax+=72;
  1472.   double x,y;
  1473.   state->clearPath();
  1474.   trans(ictm,xMin,yMin,x,y);
  1475.   state->moveTo(x,y);
  1476.   trans(ictm,xMax,yMin,x,y);
  1477.   state->lineTo(x,y);
  1478.   trans(ictm,xMax,yMax,x,y);
  1479.   state->lineTo(x,y);
  1480.   trans(ictm,xMin,yMax,x,y);
  1481.   state->lineTo(x,y);
  1482.   state->closePath();
  1483.   out->fill(state);
  1484.   state->clearPath();
  1485.  
  1486.   state = state->restore();
  1487.   out->restoreState(state);
  1488.  return;
  1489. #endif
  1490.  
  1491. //**** end Colin Granville ****
  1492.  
  1493.   // save current graphics state
  1494.   out->saveState(state);
  1495.   state = state->save();
  1496.  
  1497.   // clip to bbox
  1498.   if (shading->getHasBBox()) {
  1499.     shading->getBBox(&xMin, &yMin, &xMax, &yMax);
  1500.     state->moveTo(xMin, yMin);
  1501.     state->lineTo(xMax, yMin);
  1502.     state->lineTo(xMax, yMax);
  1503.     state->lineTo(xMin, yMax);
  1504.     state->closePath();
  1505.     state->clip();
  1506.     out->clip(state);
  1507.     state->clearPath();
  1508.   }
  1509.  
  1510.   // set the color space
  1511.   state->setFillColorSpace(shading->getColorSpace()->copy());
  1512.  
  1513.   // do shading type-specific operations
  1514.   switch (shading->getType()) {
  1515.   case 2:
  1516.     doAxialShFill((GfxAxialShading *)shading);
  1517.     break;
  1518.   case 3:
  1519.     doRadialShFill((GfxRadialShading *)shading);
  1520.     break;
  1521.   }
  1522.  
  1523.   // restore graphics state
  1524.   state = state->restore();
  1525.   out->restoreState(state);
  1526.  
  1527.   delete shading;
  1528. }
  1529.  
  1530. void Gfx::doAxialShFill(GfxAxialShading *shading) {
  1531.   double xMin, yMin, xMax, yMax;
  1532.   double x0, y0, x1, y1;
  1533.   double dx, dy, mul;
  1534.   double tMin, tMax, t, tx, ty;
  1535.   double s[4], sMin, sMax, tmp;
  1536.   double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
  1537.   double t0, t1, tt;
  1538.   double ta[axialMaxSplits + 1];
  1539.   int next[axialMaxSplits + 1];
  1540.   GfxColor color0, color1;
  1541.   int nComps;
  1542.   int i, j, k, kk;
  1543.  
  1544.   // get the clip region bbox
  1545.   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
  1546.  
  1547.   // compute min and max t values, based on the four corners of the
  1548.   // clip region bbox
  1549.   shading->getCoords(&x0, &y0, &x1, &y1);
  1550.   dx = x1 - x0;
  1551.   dy = y1 - y0;
  1552.   mul = 1 / (dx * dx + dy * dy);
  1553.   tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
  1554.   t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
  1555.   if (t < tMin) {
  1556.     tMin = t;
  1557.   } else if (t > tMax) {
  1558.     tMax = t;
  1559.   }
  1560.   t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
  1561.   if (t < tMin) {
  1562.     tMin = t;
  1563.   } else if (t > tMax) {
  1564.     tMax = t;
  1565.   }
  1566.   t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
  1567.   if (t < tMin) {
  1568.     tMin = t;
  1569.   } else if (t > tMax) {
  1570.     tMax = t;
  1571.   }
  1572.   if (tMin < 0 && !shading->getExtend0()) {
  1573.     tMin = 0;
  1574.   }
  1575.   if (tMax > 1 && !shading->getExtend1()) {
  1576.     tMax = 1;
  1577.   }
  1578.  
  1579.   // get the function domain
  1580.   t0 = shading->getDomain0();
  1581.   t1 = shading->getDomain1();
  1582.  
  1583.   // Traverse the t axis and do the shading.
  1584.   //
  1585.   // For each point (tx, ty) on the t axis, consider a line through
  1586.   // that point perpendicular to the t axis:
  1587.   //
  1588.   //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
  1589.   //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
  1590.   //
  1591.   // Then look at the intersection of this line with the bounding box
  1592.   // (xMin, yMin, xMax, yMax).  In the general case, there are four
  1593.   // intersection points:
  1594.   //
  1595.   //     s0 = (xMin - tx) / -dy
  1596.   //     s1 = (xMax - tx) / -dy
  1597.   //     s2 = (yMin - ty) / dx
  1598.   //     s3 = (yMax - ty) / dx
  1599.   //
  1600.   // and we want the middle two s values.
  1601.   //
  1602.   // In the case where dx = 0, take s0 and s1; in the case where dy =
  1603.   // 0, take s2 and s3.
  1604.   //
  1605.   // Each filled polygon is bounded by two of these line segments
  1606.   // perpdendicular to the t axis.
  1607.   //
  1608.   // The t axis is bisected into smaller regions until the color
  1609.   // difference across a region is small enough, and then the region
  1610.   // is painted with a single color.
  1611.  
  1612.   // set up
  1613.   nComps = shading->getColorSpace()->getNComps();
  1614.   ta[0] = tMin;
  1615.   ta[axialMaxSplits] = tMax;
  1616.   next[0] = axialMaxSplits;
  1617.  
  1618.   // compute the color at t = tMin
  1619.   if (tMin < 0) {
  1620.     tt = t0;
  1621.   } else if (tMin > 1) {
  1622.     tt = t1;
  1623.   } else {
  1624.     tt = t0 + (t1 - t0) * tMin;
  1625.   }
  1626.   shading->getColor(tt, &color0);
  1627.  
  1628.   // compute the coordinates of the point on the t axis at t = tMin;
  1629.   // then compute the intersection of the perpendicular line with the
  1630.   // bounding box
  1631.   tx = x0 + tMin * dx;
  1632.   ty = y0 + tMin * dy;
  1633.   if (dx == 0 && dy == 0) {
  1634.     sMin = sMax = 0;
  1635.   } if (dx == 0) {
  1636.     sMin = (xMin - tx) / -dy;
  1637.     sMax = (xMax - tx) / -dy;
  1638.     if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
  1639.   } else if (dy == 0) {
  1640.     sMin = (yMin - ty) / dx;
  1641.     sMax = (yMax - ty) / dx;
  1642.     if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
  1643.   } else {
  1644.     s[0] = (yMin - ty) / dx;
  1645.     s[1] = (yMax - ty) / dx;
  1646.     s[2] = (xMin - tx) / -dy;
  1647.     s[3] = (xMax - tx) / -dy;
  1648.     for (j = 0; j < 3; ++j) {
  1649.       kk = j;
  1650.       for (k = j + 1; k < 4; ++k) {
  1651.         if (s[k] < s[kk]) {
  1652.           kk = k;
  1653.         }
  1654.       }
  1655.       tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
  1656.     }
  1657.     sMin = s[1];
  1658.     sMax = s[2];
  1659.   }
  1660.   ux0 = tx - sMin * dy;
  1661.   uy0 = ty + sMin * dx;
  1662.   vx0 = tx - sMax * dy;
  1663.   vy0 = ty + sMax * dx;
  1664.  
  1665.   i = 0;
  1666.   while (i < axialMaxSplits) {
  1667.  
  1668.     // bisect until color difference is small enough or we hit the
  1669.     // bisection limit
  1670.     j = next[i];
  1671.     while (j > i + 1) {
  1672.       if (ta[j] < 0) {
  1673.         tt = t0;
  1674.       } else if (ta[j] > 1) {
  1675.         tt = t1;
  1676.       } else {
  1677.         tt = t0 + (t1 - t0) * ta[j];
  1678.       }
  1679.       shading->getColor(tt, &color1);
  1680.       for (k = 0; k < nComps; ++k) {
  1681.         if (fabs(color1.c[k] - color0.c[k]) > axialColorDelta) {
  1682.           break;
  1683.         }
  1684.       }
  1685.       if (k == nComps) {
  1686.         break;
  1687.       }
  1688.       k = (i + j) / 2;
  1689.       ta[k] = 0.5 * (ta[i] + ta[j]);
  1690.       next[i] = k;
  1691.       next[k] = j;
  1692.       j = k;
  1693.     }
  1694.  
  1695.     // use the average of the colors of the two sides of the region
  1696.     for (k = 0; k < nComps; ++k) {
  1697.       color0.c[k] = 0.5 * (color0.c[k] + color1.c[k]);
  1698.     }
  1699.  
  1700.     // compute the coordinates of the point on the t axis; then
  1701.     // compute the intersection of the perpendicular line with the
  1702.     // bounding box
  1703.     tx = x0 + ta[j] * dx;
  1704.     ty = y0 + ta[j] * dy;
  1705.     if (dx == 0 && dy == 0) {
  1706.       sMin = sMax = 0;
  1707.     } if (dx == 0) {
  1708.       sMin = (xMin - tx) / -dy;
  1709.       sMax = (xMax - tx) / -dy;
  1710.       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
  1711.     } else if (dy == 0) {
  1712.       sMin = (yMin - ty) / dx;
  1713.       sMax = (yMax - ty) / dx;
  1714.       if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
  1715.     } else {
  1716.       s[0] = (yMin - ty) / dx;
  1717.       s[1] = (yMax - ty) / dx;
  1718.       s[2] = (xMin - tx) / -dy;
  1719.       s[3] = (xMax - tx) / -dy;
  1720.       for (j = 0; j < 3; ++j) {
  1721.         kk = j;
  1722.         for (k = j + 1; k < 4; ++k) {
  1723.           if (s[k] < s[kk]) {
  1724.             kk = k;
  1725.           }
  1726.         }
  1727.         tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
  1728.       }
  1729.       sMin = s[1];
  1730.       sMax = s[2];
  1731.     }
  1732.     ux1 = tx - sMin * dy;
  1733.     uy1 = ty + sMin * dx;
  1734.     vx1 = tx - sMax * dy;
  1735.     vy1 = ty + sMax * dx;
  1736.  
  1737.     // set the color
  1738.     state->setFillColor(&color0);
  1739.     out->updateFillColor(state);
  1740.  
  1741.     // fill the region
  1742.     state->moveTo(ux0, uy0);
  1743.     state->lineTo(vx0, vy0);
  1744.     state->lineTo(vx1, vy1);
  1745.     state->lineTo(ux1, uy1);
  1746.     state->closePath();
  1747.     out->fill(state);
  1748.     state->clearPath();
  1749.  
  1750.     // set up for next region
  1751.     ux0 = ux1;
  1752.     uy0 = uy1;
  1753.     vx0 = vx1;
  1754.     vy0 = vy1;
  1755.     color0 = color1;
  1756.     i = next[i];
  1757.   }
  1758. }
  1759.  
  1760. void Gfx::doRadialShFill(GfxRadialShading *shading) {
  1761.   double sMin, sMax, xMin, yMin, xMax, yMax;
  1762.   double x0, y0, r0, x1, y1, r1, t0, t1;
  1763.   int nComps;
  1764.   GfxColor colorA, colorB;
  1765.   double xa, ya, xb, yb, ra, rb;
  1766.   double ta, tb, sa, sb;
  1767.   int ia, ib, k, n;
  1768.   double *ctm;
  1769.   double angle, t;
  1770.  
  1771.   // get the shading info
  1772.   shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
  1773.   t0 = shading->getDomain0();
  1774.   t1 = shading->getDomain1();
  1775.   nComps = shading->getColorSpace()->getNComps();
  1776.  
  1777.   // compute the (possibly extended) s range
  1778.   sMin = 0;
  1779.   sMax = 1;
  1780.   if (shading->getExtend0()) {
  1781.     if (r0 < r1) {
  1782.       // extend the smaller end
  1783.       sMin = -r0 / (r1 - r0);
  1784.     } else {
  1785.       // extend the larger end
  1786.       //~ this computes the diagonal of the bounding box -- we should
  1787.       //~ really compute the intersection of the moving/expanding
  1788.       //~ circles with each of the four corners and look for the max
  1789.       //~ radius
  1790.       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
  1791.       sMin = (sqrt((xMax - xMin) * (xMax - xMin) +
  1792.                    (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
  1793.       if (sMin > 0) {
  1794.         sMin = 0;
  1795.       } else if (sMin < -20) {
  1796.         // sanity check
  1797.         sMin = -20;
  1798.       }
  1799.     }
  1800.   }
  1801.   if (shading->getExtend1()) {
  1802.     if (r1 < r0) {
  1803.       // extend the smaller end
  1804.       sMax = -r0 / (r1 - r0);
  1805.     } else if (r1 > r0) {
  1806.       // extend the larger end
  1807.       state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
  1808.       sMax = (sqrt((xMax - xMin) * (xMax - xMin) +
  1809.                    (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
  1810.       if (sMax < 1) {
  1811.         sMin = 1;
  1812.       } else if (sMax > 20) {
  1813.         // sanity check
  1814.         sMax = 20;
  1815.       }
  1816.     }
  1817.   }
  1818.  
  1819.   // compute the number of steps into which circles must be divided to
  1820.   // achieve a curve flatness of 0.1 pixel in device space for the
  1821.   // largest circle (note that "device space" is 72 dpi when generating
  1822.   // PostScript, hence the relatively small 0.1 pixel accuracy)
  1823.   ctm = state->getCTM();
  1824.   t = fabs(ctm[0]);
  1825.   if (fabs(ctm[1]) > t) {
  1826.     t = fabs(ctm[1]);
  1827.   }
  1828.   if (fabs(ctm[2]) > t) {
  1829.     t = fabs(ctm[2]);
  1830.   }
  1831.   if (fabs(ctm[3]) > t) {
  1832.     t = fabs(ctm[3]);
  1833.   }
  1834.   if (r0 > r1) {
  1835.     t *= r0;
  1836.   } else {
  1837.     t *= r1;
  1838.   }
  1839.   if (t < 1) {
  1840.     n = 3;
  1841.   } else {
  1842.     n = (int)(M_PI / acos(1 - 0.1 / t));
  1843.     if (n < 3) {
  1844.       n = 3;
  1845.     } else if (n > 200) {
  1846.       n = 200;
  1847.     }
  1848.   }
  1849.  
  1850.   // Traverse the t axis and do the shading.
  1851.   //
  1852.   // This generates and fills a series of rings.  Each ring is defined
  1853.   // by two circles:
  1854.   //   sa, ta, xa, ya, ra, colorA
  1855.   //   sb, tb, xb, yb, rb, colorB
  1856.   //
  1857.   // The s/t axis is divided into radialMaxSplits parts; these parts
  1858.   // are combined as much as possible while respecting the
  1859.   // radialColorDelta parameter.
  1860.  
  1861.   // setup for the start circle
  1862.   ia = 0;
  1863.   sa = sMin;
  1864.   ta = t0 + sa * (t1 - t0);
  1865.   xa = x0 + sa * (x1 - x0);
  1866.   ya = y0 + sa * (y1 - y0);
  1867.   ra = r0 + sa * (r1 - r0);
  1868.   if (ta < t0) {
  1869.     shading->getColor(t0, &colorA);
  1870.   } else if (ta > t1) {
  1871.     shading->getColor(t1, &colorA);
  1872.   } else {
  1873.     shading->getColor(ta, &colorA);
  1874.   }
  1875.  
  1876.   while (ia < radialMaxSplits) {
  1877.  
  1878.     // go as far along the t axis (toward t1) as we can, such that the
  1879.     // color difference is within the tolerance (radialColorDelta) --
  1880.     // this uses bisection (between the current value, t, and t1),
  1881.     // limited to radialMaxSplits points along the t axis
  1882.     ib = radialMaxSplits;
  1883.     sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
  1884.     tb = t0 + sb * (t1 - t0);
  1885.     if (tb < t0) {
  1886.       shading->getColor(t0, &colorB);
  1887.     } else if (tb > t1) {
  1888.       shading->getColor(t1, &colorB);
  1889.     } else {
  1890.       shading->getColor(tb, &colorB);
  1891.     }
  1892.     while (ib - ia > 1) {
  1893.       for (k = 0; k < nComps; ++k) {
  1894.         if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
  1895.           break;
  1896.         }
  1897.       }
  1898.       if (k == nComps) {
  1899.         break;
  1900.       }
  1901.       ib = (ia + ib) / 2;
  1902.       sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
  1903.       tb = t0 + sb * (t1 - t0);
  1904.       if (tb < t0) {
  1905.         shading->getColor(t0, &colorB);
  1906.       } else if (tb > t1) {
  1907.         shading->getColor(t1, &colorB);
  1908.       } else {
  1909.         shading->getColor(tb, &colorB);
  1910.       }
  1911.     }
  1912.  
  1913.     // compute center and radius of the circle
  1914.     xb = x0 + sb * (x1 - x0);
  1915.     yb = y0 + sb * (y1 - y0);
  1916.     rb = r0 + sb * (r1 - r0);
  1917.  
  1918.     // use the average of the colors at the two circles
  1919.     for (k = 0; k < nComps; ++k) {
  1920.       colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]);
  1921.     }
  1922.     state->setFillColor(&colorA);
  1923.     out->updateFillColor(state);
  1924.  
  1925.     // construct path for first circle
  1926.     state->moveTo(xa + ra, ya);
  1927.     for (k = 1; k < n; ++k) {
  1928.       angle = ((double)k / (double)n) * 2 * M_PI;
  1929.       state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
  1930.     }
  1931.     state->closePath();
  1932.  
  1933.     // construct and append path for second circle
  1934.     state->moveTo(xb + rb, yb);
  1935.     for (k = 1; k < n; ++k) {
  1936.       angle = ((double)k / (double)n) * 2 * M_PI;
  1937.       state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
  1938.     }
  1939.     state->closePath();
  1940.  
  1941.     // fill the ring
  1942.     out->eoFill(state);
  1943.     state->clearPath();
  1944.  
  1945.     // step to the next value of t
  1946.     ia = ib;
  1947.     sa = sb;
  1948.     ta = tb;
  1949.     xa = xb;
  1950.     ya = yb;
  1951.     ra = rb;
  1952.     colorA = colorB;
  1953.   }
  1954. }
  1955.  
  1956. void Gfx::doEndPath() {
  1957.   if (state->isCurPt() && clip != clipNone) {
  1958.     state->clip();
  1959.     if (clip == clipNormal) {
  1960.       out->clip(state);
  1961.     } else {
  1962.       out->eoClip(state);
  1963.     }
  1964.   }
  1965.   clip = clipNone;
  1966.   state->clearPath();
  1967. }
  1968.  
  1969. //------------------------------------------------------------------------
  1970. // path clipping operators
  1971. //------------------------------------------------------------------------
  1972.  
  1973. void Gfx::opClip(Object args[], int numArgs) {
  1974.   clip = clipNormal;
  1975. }
  1976.  
  1977. void Gfx::opEOClip(Object args[], int numArgs) {
  1978.   clip = clipEO;
  1979. }
  1980.  
  1981. //------------------------------------------------------------------------
  1982. // text object operators
  1983. //------------------------------------------------------------------------
  1984.  
  1985. void Gfx::opBeginText(Object args[], int numArgs) {
  1986.   state->setTextMat(1, 0, 0, 1, 0, 0);
  1987.   state->textMoveTo(0, 0);
  1988.   out->updateTextMat(state);
  1989.   out->updateTextPos(state);
  1990.   fontChanged = gTrue;
  1991. }
  1992.  
  1993. void Gfx::opEndText(Object args[], int numArgs) {
  1994. }
  1995.  
  1996. //------------------------------------------------------------------------
  1997. // text state operators
  1998. //------------------------------------------------------------------------
  1999.  
  2000. void Gfx::opSetCharSpacing(Object args[], int numArgs) {
  2001.   state->setCharSpace(args[0].getNum());
  2002.   out->updateCharSpace(state);
  2003. }
  2004.  
  2005. void Gfx::opSetFont(Object args[], int numArgs) {
  2006.   GfxFont *font;
  2007.  
  2008.   if (!(font = res->lookupFont(args[0].getName()))) {
  2009.     return;
  2010.   }
  2011.   if (printCommands) {
  2012.     printf("  font: tag=%s name='%s' %g\n",
  2013.            font->getTag()->getCString(),
  2014.            font->getName() ? font->getName()->getCString() : "???",
  2015.            args[1].getNum());
  2016.     fflush(stdout);
  2017.   }
  2018.   state->setFont(font, args[1].getNum());
  2019.   fontChanged = gTrue;
  2020. }
  2021.  
  2022. void Gfx::opSetTextLeading(Object args[], int numArgs) {
  2023.   state->setLeading(args[0].getNum());
  2024. }
  2025.  
  2026. void Gfx::opSetTextRender(Object args[], int numArgs) {
  2027.   state->setRender(args[0].getInt());
  2028.   out->updateRender(state);
  2029. }
  2030.  
  2031. void Gfx::opSetTextRise(Object args[], int numArgs) {
  2032.   state->setRise(args[0].getNum());
  2033.   out->updateRise(state);
  2034. }
  2035.  
  2036. void Gfx::opSetWordSpacing(Object args[], int numArgs) {
  2037.   state->setWordSpace(args[0].getNum());
  2038.   out->updateWordSpace(state);
  2039. }
  2040.  
  2041. void Gfx::opSetHorizScaling(Object args[], int numArgs) {
  2042.   state->setHorizScaling(args[0].getNum());
  2043.   out->updateHorizScaling(state);
  2044.   fontChanged = gTrue;
  2045. }
  2046.  
  2047. //------------------------------------------------------------------------
  2048. // text positioning operators
  2049. //------------------------------------------------------------------------
  2050.  
  2051. void Gfx::opTextMove(Object args[], int numArgs) {
  2052.   double tx, ty;
  2053.  
  2054.   tx = state->getLineX() + args[0].getNum();
  2055.   ty = state->getLineY() + args[1].getNum();
  2056.   state->textMoveTo(tx, ty);
  2057.   out->updateTextPos(state);
  2058. }
  2059.  
  2060. void Gfx::opTextMoveSet(Object args[], int numArgs) {
  2061.   double tx, ty;
  2062.  
  2063.   tx = state->getLineX() + args[0].getNum();
  2064.   ty = args[1].getNum();
  2065.   state->setLeading(-ty);
  2066.   ty += state->getLineY();
  2067.   state->textMoveTo(tx, ty);
  2068.   out->updateTextPos(state);
  2069. }
  2070.  
  2071. void Gfx::opSetTextMatrix(Object args[], int numArgs) {
  2072. //**** Colin Granville ****
  2073. #ifndef ACORN
  2074.   state->setTextMat(args[0].getNum(), args[1].getNum(),
  2075.                     args[2].getNum(), args[3].getNum(),
  2076.                     args[4].getNum(), args[5].getNum());
  2077.   state->textMoveTo(0, 0);
  2078.   out->updateTextMat(state);
  2079.   out->updateTextPos(state);
  2080.   fontChanged = gTrue;
  2081. #else
  2082. //*** Colin Granville ******
  2083. // only update font if font transformation - not position - has changed
  2084.   double newTm[4];
  2085.   newTm[0] = args[0].getNum();
  2086.   newTm[1] = args[1].getNum();
  2087.   newTm[2] = args[2].getNum();
  2088.   newTm[3] = args[3].getNum();
  2089.   double*tm=state->getTextMat();
  2090.   int sameTranform = 1;
  2091.   int i;
  2092.   for (i=0;i<(sizeof(newTm)/sizeof(unsigned int));i++)
  2093.   {
  2094.     if ( ((unsigned int*)newTm)[i] != ((unsigned int*)tm)[i])
  2095.     {
  2096.       sameTranform=0;
  2097.       break;
  2098.     }
  2099.   }
  2100.   state->setTextMat(newTm[0],newTm[1],
  2101.                     newTm[2],newTm[3],
  2102.                     args[4].getNum(), args[5].getNum());
  2103.  
  2104.   state->textMoveTo(0, 0);
  2105.   out->updateTextMat(state);
  2106.   out->updateTextPos(state);
  2107.   if (!sameTranform) fontChanged = gTrue;
  2108. #endif
  2109. //**** end Colin Granville ****
  2110. }
  2111.  
  2112. void Gfx::opTextNextLine(Object args[], int numArgs) {
  2113.   double tx, ty;
  2114.  
  2115.   tx = state->getLineX();
  2116.   ty = state->getLineY() - state->getLeading();
  2117.   state->textMoveTo(tx, ty);
  2118.   out->updateTextPos(state);
  2119. }
  2120.  
  2121. //------------------------------------------------------------------------
  2122. // text string operators
  2123. //------------------------------------------------------------------------
  2124.  
  2125. void Gfx::opShowText(Object args[], int numArgs) {
  2126.   if (!state->getFont()) {
  2127.     error(getPos(), "No font in show");
  2128.     return;
  2129.   }
  2130.   doShowText(args[0].getString());
  2131. }
  2132.  
  2133. void Gfx::opMoveShowText(Object args[], int numArgs) {
  2134.   double tx, ty;
  2135.  
  2136.   if (!state->getFont()) {
  2137.     error(getPos(), "No font in move/show");
  2138.     return;
  2139.   }
  2140.   tx = state->getLineX();
  2141.   ty = state->getLineY() - state->getLeading();
  2142.   state->textMoveTo(tx, ty);
  2143.   out->updateTextPos(state);
  2144.   doShowText(args[0].getString());
  2145. }
  2146.  
  2147. void Gfx::opMoveSetShowText(Object args[], int numArgs) {
  2148.   double tx, ty;
  2149.  
  2150.   if (!state->getFont()) {
  2151.     error(getPos(), "No font in move/set/show");
  2152.     return;
  2153.   }
  2154.   state->setWordSpace(args[0].getNum());
  2155.   state->setCharSpace(args[1].getNum());
  2156.   tx = state->getLineX();
  2157.   ty = state->getLineY() - state->getLeading();
  2158.   state->textMoveTo(tx, ty);
  2159.   out->updateWordSpace(state);
  2160.   out->updateCharSpace(state);
  2161.   out->updateTextPos(state);
  2162.   doShowText(args[2].getString());
  2163. }
  2164.  
  2165. void Gfx::opShowSpaceText(Object args[], int numArgs) {
  2166.   Array *a;
  2167.   Object obj;
  2168.   int wMode;
  2169.   int i;
  2170.  
  2171.   if (!state->getFont()) {
  2172.     error(getPos(), "No font in show/space");
  2173.     return;
  2174.   }
  2175.   wMode = state->getFont()->getWMode();
  2176.   a = args[0].getArray();
  2177.   for (i = 0; i < a->getLength(); ++i) {
  2178.     a->get(i, &obj);
  2179.     if (obj.isNum()) {
  2180.       if (wMode) {
  2181.         state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize());
  2182.       } else {
  2183.         state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0);
  2184.       }
  2185.       out->updateTextShift(state, obj.getNum());
  2186.     } else if (obj.isString()) {
  2187.       doShowText(obj.getString());
  2188.     } else {
  2189.       error(getPos(), "Element of show/space array must be number or string");
  2190.     }
  2191.     obj.free();
  2192.   }
  2193. }
  2194.  
  2195. void Gfx::doShowText(GString *s) {
  2196.   GfxFont *font;
  2197.   int wMode;
  2198.   double riseX, riseY;
  2199.   CharCode code;
  2200.   Unicode u[8];
  2201.   double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
  2202.   double originX, originY, tOriginX, tOriginY;
  2203.   double oldCTM[6], newCTM[6];
  2204.   double *mat;
  2205.   Object charProc;
  2206.   Dict *resDict;
  2207.   Parser *oldParser;
  2208.   char *p;
  2209.   int len, n, uLen, nChars, nSpaces, i;
  2210.  
  2211.   if (fontChanged) {
  2212.     out->updateFont(state);
  2213.     fontChanged = gFalse;
  2214.   }
  2215.   font = state->getFont();
  2216.   wMode = font->getWMode();
  2217.  
  2218.   if (out->useDrawChar()) {
  2219.     out->beginString(state, s);
  2220.   }
  2221.  
  2222.   // handle a Type 3 char
  2223.   if (font->getType() == fontType3 && out->interpretType3Chars()) {
  2224.     mat = state->getCTM();
  2225.     for (i = 0; i < 6; ++i) {
  2226.       oldCTM[i] = mat[i];
  2227.     }
  2228.     mat = state->getTextMat();
  2229.     newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
  2230.     newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
  2231.     newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
  2232.     newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
  2233.     mat = font->getFontMatrix();
  2234.     newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
  2235.     newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
  2236.     newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
  2237.     newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
  2238.     newCTM[0] *= state->getFontSize();
  2239.     newCTM[3] *= state->getFontSize();
  2240.     newCTM[0] *= state->getHorizScaling();
  2241.     newCTM[2] *= state->getHorizScaling();
  2242.     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
  2243.     curX = state->getCurX();
  2244.     curY = state->getCurY();
  2245.     lineX = state->getLineX();
  2246.     lineY = state->getLineY();
  2247.     oldParser = parser;
  2248.     p = s->getCString();
  2249.     len = s->getLength();
  2250.     while (len > 0) {
  2251.       n = font->getNextChar(p, len, &code,
  2252.                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
  2253.                             &dx, &dy, &originX, &originY);
  2254.       dx = dx * state->getFontSize() + state->getCharSpace();
  2255.       if (n == 1 && *p == ' ') {
  2256.         dx += state->getWordSpace();
  2257.       }
  2258.       dx *= state->getHorizScaling();
  2259.       dy *= state->getFontSize();
  2260.       state->textTransformDelta(dx, dy, &tdx, &tdy);
  2261.       state->transform(curX + riseX, curY + riseY, &x, &y);
  2262.       out->saveState(state);
  2263.       state = state->save();
  2264.       state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
  2265.       //~ out->updateCTM(???)
  2266.       if (!out->beginType3Char(state, code, u, uLen)) {
  2267.         ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
  2268.         if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
  2269.           pushResources(resDict);
  2270.         }
  2271.         if (charProc.isStream()) {
  2272.           display(&charProc, gFalse);
  2273.         } else {
  2274.           error(getPos(), "Missing or bad Type3 CharProc entry");
  2275.         }
  2276.         out->endType3Char(state);
  2277.         if (resDict) {
  2278.           popResources();
  2279.         }
  2280.         charProc.free();
  2281.       }
  2282.       state = state->restore();
  2283.       out->restoreState(state);
  2284.       // GfxState::restore() does *not* restore the current position,
  2285.       // so we deal with it here using (curX, curY) and (lineX, lineY)
  2286.       curX += tdx;
  2287.       curY += tdy;
  2288.       state->moveTo(curX, curY);
  2289.       state->textSetPos(lineX, lineY);
  2290.       p += n;
  2291.       len -= n;
  2292.     }
  2293.     parser = oldParser;
  2294.  
  2295.   } else if (out->useDrawChar()) {
  2296.     state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
  2297.     p = s->getCString();
  2298.     len = s->getLength();
  2299.     while (len > 0) {
  2300.       n = font->getNextChar(p, len, &code,
  2301.                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
  2302.                             &dx, &dy, &originX, &originY);
  2303. //**** Colin Granville ******
  2304. #ifdef ACORN
  2305. // fudge to get dx info. tOriginX not used
  2306. tOriginX=dx;
  2307. #endif
  2308. //**** end Colin Granville ******
  2309.  
  2310.       if (wMode) {
  2311.         dx *= state->getFontSize();
  2312.         dy = dy * state->getFontSize() + state->getCharSpace();
  2313.         if (n == 1 && *p == ' ') {
  2314.           dy += state->getWordSpace();
  2315.         }
  2316.       } else {
  2317.         dx = dx * state->getFontSize() + state->getCharSpace();
  2318.         if (n == 1 && *p == ' ') {
  2319.           dx += state->getWordSpace();
  2320.         }
  2321.         dx *= state->getHorizScaling();
  2322.         dy *= state->getFontSize();
  2323.       }
  2324.       state->textTransformDelta(dx, dy, &tdx, &tdy);
  2325.  
  2326. //**** Colin Granville ******
  2327. #ifndef ACORN
  2328.   transformation done by font manager
  2329.       originX *= state->getFontSize();
  2330.       originY *= state->getFontSize();
  2331.       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
  2332. #endif
  2333. //**** end Colin Granville ******
  2334.  
  2335.       out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
  2336.                     tdx, tdy, tOriginX, tOriginY, code, u, uLen);
  2337.       state->shift(tdx, tdy);
  2338.       p += n;
  2339.       len -= n;
  2340.     }
  2341.  
  2342.   } else {
  2343.     dx = dy = 0;
  2344.     p = s->getCString();
  2345.     len = s->getLength();
  2346.     nChars = nSpaces = 0;
  2347.     while (len > 0) {
  2348.       n = font->getNextChar(p, len, &code,
  2349.                             u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
  2350.                             &dx2, &dy2, &originX, &originY);
  2351.       dx += dx2;
  2352.       dy += dy2;
  2353.       if (n == 1 && *p == ' ') {
  2354.         ++nSpaces;
  2355.       }
  2356.       ++nChars;
  2357.       p += n;
  2358.       len -= n;
  2359.     }
  2360.     if (wMode) {
  2361.       dx *= state->getFontSize();
  2362.       dy = dy * state->getFontSize()
  2363.            + nChars * state->getCharSpace()
  2364.            + nSpaces * state->getWordSpace();
  2365.     } else {
  2366.       dx = dx * state->getFontSize()
  2367.            + nChars * state->getCharSpace()
  2368.            + nSpaces * state->getWordSpace();
  2369.       dx *= state->getHorizScaling();
  2370.       dy *= state->getFontSize();
  2371.     }
  2372.     state->textTransformDelta(dx, dy, &tdx, &tdy);
  2373.     out->drawString(state, s);
  2374.     state->shift(tdx, tdy);
  2375.   }
  2376.  
  2377.   if (out->useDrawChar()) {
  2378.     out->endString(state);
  2379.   }
  2380.  
  2381.   updateLevel += 10 * s->getLength();
  2382. }
  2383.  
  2384. //------------------------------------------------------------------------
  2385. // XObject operators
  2386. //------------------------------------------------------------------------
  2387.  
  2388. void Gfx::opXObject(Object args[], int numArgs) {
  2389.   Object obj1, obj2, obj3, refObj;
  2390. #if OPI_SUPPORT
  2391.   Object opiDict;
  2392. #endif
  2393.  
  2394.   if (!res->lookupXObject(args[0].getName(), &obj1)) {
  2395.     return;
  2396.   }
  2397.   if (!obj1.isStream()) {
  2398.     error(getPos(), "XObject '%s' is wrong type", args[0].getName());
  2399.     obj1.free();
  2400.     return;
  2401.   }
  2402. #if OPI_SUPPORT
  2403.   obj1.streamGetDict()->lookup("OPI", &opiDict);
  2404.   if (opiDict.isDict()) {
  2405.     out->opiBegin(state, opiDict.getDict());
  2406.   }
  2407. #endif
  2408.   obj1.streamGetDict()->lookup("Subtype", &obj2);
  2409.   if (obj2.isName("Image")) {
  2410.     res->lookupXObjectNF(args[0].getName(), &refObj);
  2411.     doImage(&refObj, obj1.getStream(), gFalse);
  2412.     refObj.free();
  2413.   } else if (obj2.isName("Form")) {
  2414.     doForm(&obj1);
  2415.   } else if (obj2.isName("PS")) {
  2416.     obj1.streamGetDict()->lookup("Level1", &obj3);
  2417.     out->psXObject(obj1.getStream(),
  2418.                    obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
  2419.   } else if (obj2.isName()) {
  2420.     error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
  2421.   } else {
  2422.     error(getPos(), "XObject subtype is missing or wrong type");
  2423.   }
  2424.   obj2.free();
  2425. #if OPI_SUPPORT
  2426.   if (opiDict.isDict()) {
  2427.     out->opiEnd(state, opiDict.getDict());
  2428.   }
  2429.   opiDict.free();
  2430. #endif
  2431.   obj1.free();
  2432. }
  2433.  
  2434. void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
  2435.   Dict *dict;
  2436.   int width, height;
  2437.   int bits;
  2438.   GBool mask;
  2439.   GBool invert;
  2440.   GfxColorSpace *colorSpace;
  2441.   GfxImageColorMap *colorMap;
  2442.   Object maskObj;
  2443.   GBool haveMask;
  2444.   int maskColors[2*gfxColorMaxComps];
  2445.   Object obj1, obj2;
  2446.   int i;
  2447.  
  2448.   // get stream dict
  2449.   dict = str->getDict();
  2450.  
  2451.   // get size
  2452.   dict->lookup("Width", &obj1);
  2453.   if (obj1.isNull()) {
  2454.     obj1.free();
  2455.     dict->lookup("W", &obj1);
  2456.   }
  2457.   if (!obj1.isInt())
  2458.     goto err2;
  2459.   width = obj1.getInt();
  2460.   obj1.free();
  2461.   dict->lookup("Height", &obj1);
  2462.   if (obj1.isNull()) {
  2463.     obj1.free();
  2464.     dict->lookup("H", &obj1);
  2465.   }
  2466.   if (!obj1.isInt())
  2467.     goto err2;
  2468.   height = obj1.getInt();
  2469.   obj1.free();
  2470.  
  2471.   // image or mask?
  2472.   dict->lookup("ImageMask", &obj1);
  2473.   if (obj1.isNull()) {
  2474.     obj1.free();
  2475.     dict->lookup("IM", &obj1);
  2476.   }
  2477.   mask = gFalse;
  2478.   if (obj1.isBool())
  2479.     mask = obj1.getBool();
  2480.   else if (!obj1.isNull())
  2481.     goto err2;
  2482.   obj1.free();
  2483.  
  2484.   // bit depth
  2485.   dict->lookup("BitsPerComponent", &obj1);
  2486.   if (obj1.isNull()) {
  2487.     obj1.free();
  2488.     dict->lookup("BPC", &obj1);
  2489.   }
  2490.   if (!obj1.isInt())
  2491.     goto err2;
  2492.   bits = obj1.getInt();
  2493.   obj1.free();
  2494.  
  2495.   // display a mask
  2496.   if (mask) {
  2497.  
  2498.     // check for inverted mask
  2499.     if (bits != 1)
  2500.       goto err1;
  2501.     invert = gFalse;
  2502.     dict->lookup("Decode", &obj1);
  2503.     if (obj1.isNull()) {
  2504.       obj1.free();
  2505.       dict->lookup("D", &obj1);
  2506.     }
  2507.     if (obj1.isArray()) {
  2508.       obj1.arrayGet(0, &obj2);
  2509.       if (obj2.isInt() && obj2.getInt() == 1)
  2510.         invert = gTrue;
  2511.       obj2.free();
  2512.     } else if (!obj1.isNull()) {
  2513.       goto err2;
  2514.     }
  2515.     obj1.free();
  2516.  
  2517.     // draw it
  2518.     out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
  2519.  
  2520.   } else {
  2521.  
  2522.     // get color space and color map
  2523.     dict->lookup("ColorSpace", &obj1);
  2524.     if (obj1.isNull()) {
  2525.       obj1.free();
  2526.       dict->lookup("CS", &obj1);
  2527.     }
  2528.     if (obj1.isName()) {
  2529.       res->lookupColorSpace(obj1.getName(), &obj2);
  2530.       if (!obj2.isNull()) {
  2531.         obj1.free();
  2532.         obj1 = obj2;
  2533.       } else {
  2534.         obj2.free();
  2535.       }
  2536.     }
  2537.     colorSpace = GfxColorSpace::parse(&obj1);
  2538.     obj1.free();
  2539.     if (!colorSpace) {
  2540.       goto err1;
  2541.     }
  2542.     dict->lookup("Decode", &obj1);
  2543.     if (obj1.isNull()) {
  2544.       obj1.free();
  2545.       dict->lookup("D", &obj1);
  2546.     }
  2547.     colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
  2548.     obj1.free();
  2549.     if (!colorMap->isOk()) {
  2550.       delete colorMap;
  2551.       goto err1;
  2552.     }
  2553.  
  2554.     // get the mask
  2555.     haveMask = gFalse;
  2556.     dict->lookup("Mask", &maskObj);
  2557.     if (maskObj.isArray()) {
  2558.       for (i = 0; i < maskObj.arrayGetLength(); ++i) {
  2559.         maskObj.arrayGet(i, &obj1);
  2560.         maskColors[i] = obj1.getInt();
  2561.         obj1.free();
  2562.       }
  2563.       haveMask = gTrue;
  2564.     }
  2565.  
  2566.     // draw it
  2567.     out->drawImage(state, ref, str, width, height, colorMap,
  2568.                    haveMask ? maskColors : (int *)NULL,  inlineImg);
  2569.     delete colorMap;
  2570.  
  2571.     maskObj.free();
  2572.   }
  2573.  
  2574.   if ((i = width * height) > 1000) {
  2575.     i = 1000;
  2576.   }
  2577.   updateLevel += i;
  2578.  
  2579.   return;
  2580.  
  2581.  err2:
  2582.   obj1.free();
  2583.  err1:
  2584.   error(getPos(), "Bad image parameters");
  2585. }
  2586.  
  2587. void Gfx::doForm(Object *str) {
  2588.   Dict *dict;
  2589.   Object matrixObj, bboxObj;
  2590.   double m[6], bbox[6];
  2591.   Object resObj;
  2592.   Dict *resDict;
  2593.   Object obj1;
  2594.   int i;
  2595.  
  2596.   // get stream dict
  2597.   dict = str->streamGetDict();
  2598.  
  2599.   // check form type
  2600.   dict->lookup("FormType", &obj1);
  2601.   if (!(obj1.isInt() && obj1.getInt() == 1)) {
  2602.     error(getPos(), "Unknown form type");
  2603.   }
  2604.   obj1.free();
  2605.  
  2606.   // get bounding box
  2607.   dict->lookup("BBox", &bboxObj);
  2608.   if (!bboxObj.isArray()) {
  2609.     matrixObj.free();
  2610.     bboxObj.free();
  2611.     error(getPos(), "Bad form bounding box");
  2612.     return;
  2613.   }
  2614.   for (i = 0; i < 4; ++i) {
  2615.     bboxObj.arrayGet(i, &obj1);
  2616.     bbox[i] = obj1.getNum();
  2617.     obj1.free();
  2618.   }
  2619.   bboxObj.free();
  2620.  
  2621.   // get matrix
  2622.   dict->lookup("Matrix", &matrixObj);
  2623.   if (matrixObj.isArray()) {
  2624.     for (i = 0; i < 6; ++i) {
  2625.       matrixObj.arrayGet(i, &obj1);
  2626.       m[i] = obj1.getNum();
  2627.       obj1.free();
  2628.     }
  2629.   } else {
  2630.     m[0] = 1; m[1] = 0;
  2631.     m[2] = 0; m[3] = 1;
  2632.     m[4] = 0; m[5] = 0;
  2633.   }
  2634.   matrixObj.free();
  2635.  
  2636.   // get resources
  2637.   dict->lookup("Resources", &resObj);
  2638.   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
  2639.  
  2640.   // draw it
  2641.   doForm1(str, resDict, m, bbox);
  2642.  
  2643.   resObj.free();
  2644. }
  2645.  
  2646. void Gfx::doAnnot(Object *str, double xMin, double yMin,
  2647.                   double xMax, double yMax) {
  2648.   Dict *dict, *resDict;
  2649.   Object matrixObj, bboxObj, resObj;
  2650.   Object obj1;
  2651.   double m[6], bbox[6], ictm[6];
  2652.   double *ctm;
  2653.   double formX0, formY0, formX1, formY1;
  2654.   double annotX0, annotY0, annotX1, annotY1;
  2655.   double det, x, y, sx, sy;
  2656.   int i;
  2657.  
  2658.   // get stream dict
  2659.   dict = str->streamGetDict();
  2660.  
  2661.   // get the form bounding box
  2662.   dict->lookup("BBox", &bboxObj);
  2663.   if (!bboxObj.isArray()) {
  2664.     bboxObj.free();
  2665.     error(getPos(), "Bad form bounding box");
  2666.     return;
  2667.   }
  2668.   for (i = 0; i < 4; ++i) {
  2669.     bboxObj.arrayGet(i, &obj1);
  2670.     bbox[i] = obj1.getNum();
  2671.     obj1.free();
  2672.   }
  2673.   bboxObj.free();
  2674.  
  2675.   // get the form matrix
  2676.   dict->lookup("Matrix", &matrixObj);
  2677.   if (matrixObj.isArray()) {
  2678.     for (i = 0; i < 6; ++i) {
  2679.       matrixObj.arrayGet(i, &obj1);
  2680.       m[i] = obj1.getNum();
  2681.       obj1.free();
  2682.     }
  2683.   } else {
  2684.     m[0] = 1; m[1] = 0;
  2685.     m[2] = 0; m[3] = 1;
  2686.     m[4] = 0; m[5] = 0;
  2687.   }
  2688.   matrixObj.free();
  2689.  
  2690.   // transform the form bbox from form space to user space
  2691.   formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
  2692.   formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
  2693.   formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
  2694.   formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
  2695.  
  2696.   // transform the annotation bbox from default user space to user
  2697.   // space: (bbox * baseMatrix) * iCTM
  2698.   ctm = state->getCTM();
  2699.   det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
  2700.   ictm[0] = ctm[3] * det;
  2701.   ictm[1] = -ctm[1] * det;
  2702.   ictm[2] = -ctm[2] * det;
  2703.   ictm[3] = ctm[0] * det;
  2704.   ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
  2705.   ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
  2706.   x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
  2707.   y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
  2708.   annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
  2709.   annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
  2710.   x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
  2711.   y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
  2712.   annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
  2713.   annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
  2714.  
  2715.   // swap min/max coords
  2716.   if (formX0 > formX1) {
  2717.     x = formX0; formX0 = formX1; formX1 = x;
  2718.   }
  2719.   if (formY0 > formY1) {
  2720.     y = formY0; formY0 = formY1; formY1 = y;
  2721.   }
  2722.   if (annotX0 > annotX1) {
  2723.     x = annotX0; annotX0 = annotX1; annotX1 = x;
  2724.   }
  2725.   if (annotY0 > annotY1) {
  2726.     y = annotY0; annotY0 = annotY1; annotY1 = y;
  2727.   }
  2728.  
  2729.   // scale the form to fit the annotation bbox
  2730.   if (formX1 == formX0) {
  2731.     // this shouldn't happen
  2732.     sx = 1;
  2733.   } else {
  2734.     sx = (annotX1 - annotX0) / (formX1 - formX0);
  2735.   }
  2736.   if (formY1 == formY0) {
  2737.     // this shouldn't happen
  2738.     sy = 1;
  2739.   } else {
  2740.     sy = (annotY1 - annotY0) / (formY1 - formY0);
  2741.   }
  2742.   m[0] *= sx;
  2743.   m[2] *= sx;
  2744.   m[4] = (m[4] - formX0) * sx + annotX0;
  2745.   m[1] *= sy;
  2746.   m[3] *= sy;
  2747.   m[5] = (m[5] - formY0) * sy + annotY0;
  2748.  
  2749.   // get resources
  2750.   dict->lookup("Resources", &resObj);
  2751.   resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
  2752.  
  2753.   // draw it
  2754.   doForm1(str, resDict, m, bbox);
  2755.  
  2756.   resObj.free();
  2757.   bboxObj.free();
  2758. }
  2759.  
  2760. void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
  2761.   Parser *oldParser;
  2762.   double oldBaseMatrix[6];
  2763.   int i;
  2764.  
  2765.   // push new resources on stack
  2766.   pushResources(resDict);
  2767.  
  2768.   // save current graphics state
  2769.   out->saveState(state);
  2770.   state = state->save();
  2771.  
  2772.   // save current parser
  2773.   oldParser = parser;
  2774.  
  2775.   // set form transformation matrix
  2776.   state->concatCTM(matrix[0], matrix[1], matrix[2],
  2777.                    matrix[3], matrix[4], matrix[5]);
  2778.   out->updateCTM(state, matrix[0], matrix[1], matrix[2],
  2779.                  matrix[3], matrix[4], matrix[5]);
  2780.  
  2781.   // set new base matrix
  2782.   for (i = 0; i < 6; ++i) {
  2783.     oldBaseMatrix[i] = baseMatrix[i];
  2784.     baseMatrix[i] = state->getCTM()[i];
  2785.   }
  2786.  
  2787.   // set form bounding box
  2788.   state->moveTo(bbox[0], bbox[1]);
  2789.   state->lineTo(bbox[2], bbox[1]);
  2790.   state->lineTo(bbox[2], bbox[3]);
  2791.   state->lineTo(bbox[0], bbox[3]);
  2792.   state->closePath();
  2793.   state->clip();
  2794.   out->clip(state);
  2795.   state->clearPath();
  2796.  
  2797.   // draw the form
  2798.   display(str, gFalse);
  2799.  
  2800.   // restore base matrix
  2801.   for (i = 0; i < 6; ++i) {
  2802.     baseMatrix[i] = oldBaseMatrix[i];
  2803.   }
  2804.  
  2805.   // restore parser
  2806.   parser = oldParser;
  2807.  
  2808.   // restore graphics state
  2809.   state = state->restore();
  2810.   out->restoreState(state);
  2811.  
  2812.   // pop resource stack
  2813.   popResources();
  2814.  
  2815.   return;
  2816. }
  2817.  
  2818. void Gfx::pushResources(Dict *resDict) {
  2819.   res = new GfxResources(xref, resDict, res);
  2820. }
  2821.  
  2822. void Gfx::popResources() {
  2823.   GfxResources *resPtr;
  2824.  
  2825.   resPtr = res->getNext();
  2826.   delete res;
  2827.   res = resPtr;
  2828. }
  2829.  
  2830. //------------------------------------------------------------------------
  2831. // in-line image operators
  2832. //------------------------------------------------------------------------
  2833.  
  2834. void Gfx::opBeginImage(Object args[], int numArgs) {
  2835.   Stream *str;
  2836.   int c1, c2;
  2837.  
  2838.   // build dict/stream
  2839.   str = buildImageStream();
  2840.  
  2841.   // display the image
  2842.   if (str) {
  2843.     doImage(NULL, str, gTrue);
  2844.   
  2845.     // skip 'EI' tag
  2846.     c1 = str->getBaseStream()->getChar();
  2847.     c2 = str->getBaseStream()->getChar();
  2848.     while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
  2849.       c1 = c2;
  2850.       c2 = str->getBaseStream()->getChar();
  2851.     }
  2852.     delete str;
  2853.   }
  2854. }
  2855.  
  2856. Stream *Gfx::buildImageStream() {
  2857.   Object dict;
  2858.   Object obj;
  2859.   char *key;
  2860.   Stream *str;
  2861.  
  2862.   // build dictionary
  2863.   dict.initDict(xref);
  2864.   parser->getObj(&obj);
  2865.   while (!obj.isCmd("ID") && !obj.isEOF()) {
  2866.     if (!obj.isName()) {
  2867.       error(getPos(), "Inline image dictionary key must be a name object");
  2868.       obj.free();
  2869.     } else {
  2870.       key = copyString(obj.getName());
  2871.       obj.free();
  2872.       parser->getObj(&obj);
  2873.       if (obj.isEOF() || obj.isError()) {
  2874.         gfree(key);
  2875.         break;
  2876.       }
  2877.       dict.dictAdd(key, &obj);
  2878.     }
  2879.     parser->getObj(&obj);
  2880.   }
  2881.   if (obj.isEOF()) {
  2882.     error(getPos(), "End of file in inline image");
  2883.     obj.free();
  2884.     dict.free();
  2885.     return NULL;
  2886.   }
  2887.   obj.free();
  2888.  
  2889.   // make stream
  2890.   str = new EmbedStream(parser->getStream(), &dict);
  2891.   str = str->addFilters(&dict);
  2892.  
  2893.   return str;
  2894. }
  2895.  
  2896. void Gfx::opImageData(Object args[], int numArgs) {
  2897.   error(getPos(), "Internal: got 'ID' operator");
  2898. }
  2899.  
  2900. void Gfx::opEndImage(Object args[], int numArgs) {
  2901.   error(getPos(), "Internal: got 'EI' operator");
  2902. }
  2903.  
  2904. //------------------------------------------------------------------------
  2905. // type 3 font operators
  2906. //------------------------------------------------------------------------
  2907.  
  2908. void Gfx::opSetCharWidth(Object args[], int numArgs) {
  2909.   out->type3D0(state, args[0].getNum(), args[1].getNum());
  2910. }
  2911.  
  2912. void Gfx::opSetCacheDevice(Object args[], int numArgs) {
  2913.   out->type3D1(state, args[0].getNum(), args[1].getNum(),
  2914.                args[2].getNum(), args[3].getNum(),
  2915.                args[4].getNum(), args[5].getNum());
  2916. }
  2917.  
  2918. //------------------------------------------------------------------------
  2919. // compatibility operators
  2920. //------------------------------------------------------------------------
  2921.  
  2922. void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
  2923.   ++ignoreUndef;
  2924. }
  2925.  
  2926. void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
  2927.   if (ignoreUndef > 0)
  2928.     --ignoreUndef;
  2929. }
  2930.  
  2931. //------------------------------------------------------------------------
  2932. // marked content operators
  2933. //------------------------------------------------------------------------
  2934.  
  2935. void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
  2936.   if (printCommands) {
  2937.     printf("  marked content: %s ", args[0].getName());
  2938.     if (numArgs == 2)
  2939.       args[2].print(stdout);
  2940.     printf("\n");
  2941.     fflush(stdout);
  2942.   }
  2943. }
  2944.  
  2945. void Gfx::opEndMarkedContent(Object args[], int numArgs) {
  2946. }
  2947.  
  2948. void Gfx::opMarkPoint(Object args[], int numArgs) {
  2949.   if (printCommands) {
  2950.     printf("  mark point: %s ", args[0].getName());
  2951.     if (numArgs == 2)
  2952.       args[2].print(stdout);
  2953.     printf("\n");
  2954.     fflush(stdout);
  2955.   }
  2956. }
  2957.